回答編集履歴

6

追記

2018/07/10 05:16

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -121,3 +121,97 @@
121
121
  main()
122
122
 
123
123
  ```
124
+
125
+ ---
126
+
127
+
128
+
129
+ > AttributeErrorでread-onlyが出た場合の対処を教えていただけませんか?
130
+
131
+
132
+
133
+ 1, まずエラーメッセージ +openpyxl 「AttributeError: 'EmptyCell' object attribute 'value' is read-only openpyxl 」で検索します。
134
+
135
+ そうするとエクセルブックが読み取り専用になったときに発生するというのがわかります。
136
+
137
+ [AttributeError with openpyxl](https://stackoverflow.com/questions/45220078/attributeerror-with-openpyxl)
138
+
139
+ 2, 次に仮定します。
140
+
141
+ エクセルが読み取り専用になることはこのコードと実行環境上ありえるのかどうか?
142
+
143
+ →ここらへんは直感の部分もあります。
144
+
145
+
146
+
147
+ ありえないと判断したら→次は[公式ドキュメント](https://openpyxl.readthedocs.io/en/stable/index.html#)をみます。
148
+
149
+ 公式ドキュメントの探し方はopenpyxl docとかあとはopenpyxl githubで検索すると大体ヒットするかと。
150
+
151
+ 3, 公式ドキュメントのload_workbook部分のコードを検索します。
152
+
153
+ [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)がヒットします
154
+
155
+
156
+
157
+ 質問文のコードと引数を見比べます。
158
+
159
+ マニュアルを見ると第二引数read_onlyは`True`/`False`ですが、質問文は'w'で渡しています。
160
+
161
+ ```Python
162
+
163
+ wb = op.load_workbook('スクレイピング.xlsx','w')
164
+
165
+ ```
166
+
167
+
168
+
169
+ 怪しいですよね。次に仮定します。
170
+
171
+ 第二引数に'w'が渡ったときに`load_workbook`はどうなるか。
172
+
173
+ [ドキュメント](https://openpyxl.readthedocs.io/en/stable/api/openpyxl.reader.excel.html?highlight=load_workbook)の関数部分のsourceと書いてある部分をクリックしてソースコードを確認します。
174
+
175
+ コードが以下のようになっています。
176
+
177
+ ```Python
178
+
179
+ if read_only:
180
+
181
+ ws = ReadOnlyWorksheet(wb, sheet_name, worksheet_path, None,
182
+
183
+ shared_strings)
184
+
185
+
186
+
187
+ wb._sheets.append(ws)
188
+
189
+
190
+
191
+ ```
192
+
193
+ ミニマムコードを書いてみて確認します。
194
+
195
+ ```Python
196
+
197
+ def ssss(read_only=False):
198
+
199
+ if read_only:
200
+
201
+ print('done')
202
+
203
+
204
+
205
+ ssss('w')
206
+
207
+ ```
208
+
209
+ `done`が出力されました。ということは`ReadOnlyWorksheet`の部分を通ったということです。
210
+
211
+
212
+
213
+ 原因:load_workbookの第2引数に'w'を渡したためそのため、`read_only=True`と判断されて、`AttributeError: 'EmptyCell' object attribute 'value' is read-only`となったのです。
214
+
215
+ 解決方法は回答文のコードか第二引数を削除する(未検証)でも可能かと。
216
+
217
+ 以上です。

5

追記

2018/07/10 05:16

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -59,3 +59,65 @@
59
59
  wb.save('スクレイピング.xlsx') # ←インデントを一段下げる
60
60
 
61
61
  ```
62
+
63
+ ---
64
+
65
+ 変数名を変更したソースコードです。
66
+
67
+ ```Python
68
+
69
+ # -*- coding: utf-8 -*-
70
+
71
+ from urllib import request
72
+
73
+ from bs4 import BeautifulSoup
74
+
75
+ import requests
76
+
77
+ from urllib.parse import urljoin
78
+
79
+ import openpyxl as op
80
+
81
+
82
+
83
+ def main() -> None:
84
+
85
+ base_url = "https://docs.python.org/3/"
86
+
87
+ html = request.urlopen(base_url)
88
+
89
+
90
+
91
+ soup = BeautifulSoup(html,'html.parser')
92
+
93
+
94
+
95
+ wb = op.Workbook() # 変更
96
+
97
+ ws = wb.active
98
+
99
+ i = 1
100
+
101
+ for a_tag in soup.find_all('a'):
102
+
103
+ href = (urljoin(base_url, a_tag.get('href')))
104
+
105
+ if href.startswith('javascript'):
106
+
107
+ continue
108
+
109
+ ws['A'+ str(i)].value = href
110
+
111
+ i += 1
112
+
113
+ wb.save('スクレイピング.xlsx')
114
+
115
+
116
+
117
+
118
+
119
+ if __name__ == "__main__":
120
+
121
+ main()
122
+
123
+ ```

4

追記

2018/07/10 04:21

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -44,7 +44,7 @@
44
44
 
45
45
  i = 1
46
46
 
47
- for a_tag in soup.find_all('a'):
47
+ for a_tag in soup.find_all('a'): # iから適切な名前に変える
48
48
 
49
49
  j = (urljoin(base_url, a_tag.get('href')))
50
50
 

3

追記

2018/07/10 04:13

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -10,13 +10,39 @@
10
10
 
11
11
  エクセルのセルに値を設定したいという要件だと思うので以下のコードでどうでしょうか。
12
12
 
13
- エクセルの行番号は1から始まるので
13
+
14
14
 
15
15
  [Python openpyxlでExcelを操作](https://qiita.com/tftf/items/07e4332293c2c59799d1)
16
16
 
17
17
  ```diff
18
18
 
19
+ -ws = ['A'+str(i)].value = j
20
+
21
+ +ws['A'+str(i)].value = j
22
+
23
+ ```
24
+
25
+ ---
26
+
27
+ エクセルのセルの行番号は1から始まるので以下のコードだと、`soup.find_all`の列挙内容が変数:`i`に入ります。
28
+
29
+ ```Python
30
+
31
+ for i in soup.find_all('a'):
32
+
33
+ print(i) # ←print文を追加して変数iの内容を確認してみてくださいな
34
+
35
+ # 中略
36
+
37
+ ws['A'+str(i)].value = j
38
+
39
+ ```
40
+
41
+
42
+
43
+ ```Python
44
+
19
- i = 0
45
+ i = 1
20
46
 
21
47
  for a_tag in soup.find_all('a'):
22
48
 
@@ -26,10 +52,10 @@
26
52
 
27
53
  continue
28
54
 
55
+ ws['A'+ str(i)].value = j
56
+
29
57
  i += 1
30
58
 
31
- ws['A'+ str(i)].value = j
32
-
33
- wb.save('スクレイピング.xlsx')
59
+ wb.save('スクレイピング.xlsx') # ←インデントを一段下げる
34
60
 
35
61
  ```

2

追記

2018/07/10 04:12

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -10,14 +10,26 @@
10
10
 
11
11
  エクセルのセルに値を設定したいという要件だと思うので以下のコードでどうでしょうか。
12
12
 
13
-
13
+ エクセルの行番号は1から始まるので
14
14
 
15
15
  [Python openpyxlでExcelを操作](https://qiita.com/tftf/items/07e4332293c2c59799d1)
16
16
 
17
17
  ```diff
18
18
 
19
- -ws = ['A'+str(i)].value = j
19
+ i = 0
20
20
 
21
+ for a_tag in soup.find_all('a'):
22
+
23
+ j = (urljoin(base_url, a_tag.get('href')))
24
+
25
+ if j.startswith('javascript'):
26
+
27
+ continue
28
+
29
+ i += 1
30
+
21
- +ws['A'+str(i)].value = j
31
+ ws['A'+ str(i)].value = j
32
+
33
+ wb.save('スクレイピング.xlsx')
22
34
 
23
35
  ```

1

追記

2018/07/10 04:00

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -18,6 +18,6 @@
18
18
 
19
19
  -ws = ['A'+str(i)].value = j
20
20
 
21
- +ws.['A'+str(i)].value = j
21
+ +ws['A'+str(i)].value = j
22
22
 
23
23
  ```