teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

6

追記

2018/07/10 05:16

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -59,4 +59,51 @@
59
59
 
60
60
  if __name__ == "__main__":
61
61
  main()
62
- ```
62
+ ```
63
+ ---
64
+
65
+ > AttributeErrorでread-onlyが出た場合の対処を教えていただけませんか?
66
+
67
+ 1, まずエラーメッセージ +openpyxl 「AttributeError: 'EmptyCell' object attribute 'value' is read-only openpyxl 」で検索します。
68
+ そうするとエクセルブックが読み取り専用になったときに発生するというのがわかります。
69
+ [AttributeError with openpyxl](https://stackoverflow.com/questions/45220078/attributeerror-with-openpyxl)
70
+ 2, 次に仮定します。
71
+ エクセルが読み取り専用になることはこのコードと実行環境上ありえるのかどうか?
72
+ →ここらへんは直感の部分もあります。
73
+
74
+ ありえないと判断したら→次は[公式ドキュメント](https://openpyxl.readthedocs.io/en/stable/index.html#)をみます。
75
+ 公式ドキュメントの探し方はopenpyxl docとかあとはopenpyxl githubで検索すると大体ヒットするかと。
76
+ 3, 公式ドキュメントのload_workbook部分のコードを検索します。
77
+ [Search Results load_workbook](https://openpyxl.readthedocs.io/en/stable/search.html?q=load_workbook&check_keywords=yes&area=default)そうすると、この[ドキュメント](https://openpyxl.readthedocs.io/en/stable/api/openpyxl.reader.excel.html?highlight=load_workbook)がヒットします
78
+
79
+ 質問文のコードと引数を見比べます。
80
+ マニュアルを見ると第二引数read_onlyは`True`/`False`ですが、質問文は'w'で渡しています。
81
+ ```Python
82
+ wb = op.load_workbook('スクレイピング.xlsx','w')
83
+ ```
84
+
85
+ 怪しいですよね。次に仮定します。
86
+ 第二引数に'w'が渡ったときに`load_workbook`はどうなるか。
87
+ [ドキュメント](https://openpyxl.readthedocs.io/en/stable/api/openpyxl.reader.excel.html?highlight=load_workbook)の関数部分のsourceと書いてある部分をクリックしてソースコードを確認します。
88
+ コードが以下のようになっています。
89
+ ```Python
90
+ if read_only:
91
+ ws = ReadOnlyWorksheet(wb, sheet_name, worksheet_path, None,
92
+ shared_strings)
93
+
94
+ wb._sheets.append(ws)
95
+
96
+ ```
97
+ ミニマムコードを書いてみて確認します。
98
+ ```Python
99
+ def ssss(read_only=False):
100
+ if read_only:
101
+ print('done')
102
+
103
+ ssss('w')
104
+ ```
105
+ `done`が出力されました。ということは`ReadOnlyWorksheet`の部分を通ったということです。
106
+
107
+ 原因:load_workbookの第2引数に'w'を渡したためそのため、`read_only=True`と判断されて、`AttributeError: 'EmptyCell' object attribute 'value' is read-only`となったのです。
108
+ 解決方法は回答文のコードか第二引数を削除する(未検証)でも可能かと。
109
+ 以上です。

5

追記

2018/07/10 05:16

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -28,4 +28,35 @@
28
28
  ws['A'+ str(i)].value = j
29
29
  i += 1
30
30
  wb.save('スクレイピング.xlsx') # ←インデントを一段下げる
31
+ ```
32
+ ---
33
+ 変数名を変更したソースコードです。
34
+ ```Python
35
+ # -*- coding: utf-8 -*-
36
+ from urllib import request
37
+ from bs4 import BeautifulSoup
38
+ import requests
39
+ from urllib.parse import urljoin
40
+ import openpyxl as op
41
+
42
+ def main() -> None:
43
+ base_url = "https://docs.python.org/3/"
44
+ html = request.urlopen(base_url)
45
+
46
+ soup = BeautifulSoup(html,'html.parser')
47
+
48
+ wb = op.Workbook() # 変更
49
+ ws = wb.active
50
+ i = 1
51
+ for a_tag in soup.find_all('a'):
52
+ href = (urljoin(base_url, a_tag.get('href')))
53
+ if href.startswith('javascript'):
54
+ continue
55
+ ws['A'+ str(i)].value = href
56
+ i += 1
57
+ wb.save('スクレイピング.xlsx')
58
+
59
+
60
+ if __name__ == "__main__":
61
+ main()
31
62
  ```

4

追記

2018/07/10 04:21

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -21,7 +21,7 @@
21
21
 
22
22
  ```Python
23
23
  i = 1
24
- for a_tag in soup.find_all('a'):
24
+ for a_tag in soup.find_all('a'): # iから適切な名前に変える
25
25
  j = (urljoin(base_url, a_tag.get('href')))
26
26
  if j.startswith('javascript'):
27
27
  continue

3

追記

2018/07/10 04:13

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -4,15 +4,28 @@
4
4
  エラーメッセージはlist型にvalue属性が存在しないというエラーですが。
5
5
  [] ←list内包表記
6
6
  エクセルのセルに値を設定したいという要件だと思うので以下のコードでどうでしょうか。
7
- エクセルの行番号は1から始まるので
7
+
8
8
  [Python openpyxlでExcelを操作](https://qiita.com/tftf/items/07e4332293c2c59799d1)
9
9
  ```diff
10
+ -ws = ['A'+str(i)].value = j
11
+ +ws['A'+str(i)].value = j
12
+ ```
13
+ ---
14
+ エクセルのセルの行番号は1から始まるので以下のコードだと、`soup.find_all`の列挙内容が変数:`i`に入ります。
15
+ ```Python
16
+ for i in soup.find_all('a'):
17
+ print(i) # ←print文を追加して変数iの内容を確認してみてくださいな
18
+ # 中略
19
+ ws['A'+str(i)].value = j
20
+ ```
21
+
22
+ ```Python
10
- i = 0
23
+ i = 1
11
24
  for a_tag in soup.find_all('a'):
12
25
  j = (urljoin(base_url, a_tag.get('href')))
13
26
  if j.startswith('javascript'):
14
27
  continue
28
+ ws['A'+ str(i)].value = j
15
29
  i += 1
16
- ws['A'+ str(i)].value = j
17
- wb.save('スクレイピング.xlsx')
30
+ wb.save('スクレイピング.xlsx') # ←インデントを一段下げる
18
31
  ```

2

追記

2018/07/10 04:12

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -4,9 +4,15 @@
4
4
  エラーメッセージはlist型にvalue属性が存在しないというエラーですが。
5
5
  [] ←list内包表記
6
6
  エクセルのセルに値を設定したいという要件だと思うので以下のコードでどうでしょうか。
7
-
7
+ エクセルの行番号は1から始まるので
8
8
  [Python openpyxlでExcelを操作](https://qiita.com/tftf/items/07e4332293c2c59799d1)
9
9
  ```diff
10
+ i = 0
11
+ for a_tag in soup.find_all('a'):
12
+ j = (urljoin(base_url, a_tag.get('href')))
10
- -ws = ['A'+str(i)].value = j
13
+ if j.startswith('javascript'):
14
+ continue
15
+ i += 1
11
- +ws['A'+str(i)].value = j
16
+ ws['A'+ str(i)].value = j
17
+ wb.save('スクレイピング.xlsx')
12
18
  ```

1

追記

2018/07/10 04:00

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -8,5 +8,5 @@
8
8
  [Python openpyxlでExcelを操作](https://qiita.com/tftf/items/07e4332293c2c59799d1)
9
9
  ```diff
10
10
  -ws = ['A'+str(i)].value = j
11
- +ws.['A'+str(i)].value = j
11
+ +ws['A'+str(i)].value = j
12
12
  ```