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

回答編集履歴

10

関数の定義順を呼び出し順に変更

2018/07/23 13:29

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -162,7 +162,7 @@
162
162
  ```
163
163
  ---
164
164
 
165
- 以下の`False`の仕様がよく分かりませんでしたので、その部分がうまく実装できてないと思いますが。
165
+ 以下の`False`の仕様がよく分かりませんでしたので、その部分がうまく実装できてないすが。
166
166
  質問文の画像を見る限りこういうふうに出力したいのでしょうか。
167
167
  ```Python
168
168
  if not has_pdf_link:
@@ -189,20 +189,36 @@
189
189
  import time
190
190
 
191
191
 
192
- def change_window(browser):
192
+ def get_search_keyword():
193
+ """
194
+ エクセルファイルを開き、検索キーワードを取得する。
195
+ """
196
+ # テスト用
197
+ #yield "血液照射装置"
198
+ #yield "放射性医薬品合成設備"
193
- all_handles = set(browser.window_handles)
199
+ from contextlib import closing
194
- switch_to = all_handles - set([browser.current_window_handle])
200
+ with closing(op.load_workbook('一般名称.xlsx')) as wb:
195
- assert len(switch_to) == 1
201
+ for i in range(1, 9):
202
+ ws = wb.active
196
- browser.switch_to.window(*switch_to)
203
+ yield ws['A' + str(i)].value
197
204
 
198
-
205
+
199
- def get_content(word):
206
+ def get_content(word: str) -> tuple:
200
207
  """
201
208
  スクレイピングする。
202
209
  :param word 検索キーワード
203
210
  :return スクレイピング結果(HTML)とURL
204
211
  ※ chromedriver.exe をCドライブ直下に置くこと。
205
212
  """
213
+ def change_window(browser):
214
+ """
215
+ ブラウザのウィンドウを切り替える。
216
+ """
217
+ all_handles = set(browser.window_handles)
218
+ switch_to = all_handles - set([browser.current_window_handle])
219
+ assert len(switch_to) == 1
220
+ browser.switch_to.window(*switch_to)
221
+
206
222
  driver = webdriver.Chrome(r'C:/chromedriver.exe')
207
223
  driver.get("https://www.pmda.go.jp/PmdaSearch/kikiSearch/")
208
224
  # id検索
@@ -216,23 +232,11 @@
216
232
  # print(driver.page_source)
217
233
  html = driver.page_source
218
234
  cur_url = driver.current_url
235
+ driver.quit()
236
+
219
237
  return html, cur_url
220
238
 
221
239
 
222
- def get_search_keyword():
223
- """
224
- エクセルファイルを開き、検索キーワードを取得する。
225
- """
226
- # テスト用
227
- #yield "血液照射装置"
228
- #yield "放射性医薬品合成設備"
229
- from contextlib import closing
230
- with closing(op.load_workbook('一般名称.xlsx')) as wb:
231
- for i in range(1, 9):
232
- ws = wb.active
233
- yield ws['A' + str(i)].value
234
-
235
-
236
240
  def parse(soup, cur_url: str):
237
241
  """
238
242
  スクレイピング結果を解析
@@ -262,6 +266,7 @@
262
266
 
263
267
  #wb.save('URL_DATA.xlsx')
264
268
 
269
+
265
270
  def main():
266
271
  START_ROW = 0
267
272
  row_index = 1
@@ -271,9 +276,7 @@
271
276
  output = []
272
277
  time_data = datetime.datetime.today()
273
278
  for i, (has_pdf_link, link_pdf) in enumerate(parse(soup, cur_url), start=START_ROW):
274
- word_col = ""
279
+ word_col = word if i == START_ROW else ""
275
- if i == START_ROW:
276
- word_col = word
277
280
  output.append([time_data, link_pdf, word_col])
278
281
 
279
282
  output_excel(output, row_index)
@@ -282,4 +285,5 @@
282
285
 
283
286
  if __name__ == "__main__":
284
287
  main()
288
+
285
289
  ```

9

補足

2018/07/23 13:29

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -159,4 +159,127 @@
159
159
 
160
160
  if __name__ == "__main__":
161
161
  main()
162
+ ```
163
+ ---
164
+
165
+ 以下の`False`の仕様がよく分かりませんでしたので、その部分がうまく実装できてないと思いますが。
166
+ 質問文の画像を見る限りこういうふうに出力したいのでしょうか。
167
+ ```Python
168
+ if not has_pdf_link:
169
+ print('False')
170
+ ws['B'+str(i)].value = has_pdf_link
171
+
172
+ time.sleep(2)
173
+ time_data = datetime.datetime.today()
174
+
175
+ ws['A'+str(i)].value = time_data
176
+ ```
177
+
178
+ ```Python
179
+ # -*- coding: utf-8 -*-
180
+ from selenium import webdriver
181
+ from selenium.webdriver.common.by import By
182
+ from selenium.webdriver.support.ui import WebDriverWait
183
+ from urllib import request
184
+ from bs4 import BeautifulSoup
185
+ import requests
186
+ from urllib.parse import urljoin
187
+ import openpyxl as op
188
+ import datetime
189
+ import time
190
+
191
+
192
+ def change_window(browser):
193
+ all_handles = set(browser.window_handles)
194
+ switch_to = all_handles - set([browser.current_window_handle])
195
+ assert len(switch_to) == 1
196
+ browser.switch_to.window(*switch_to)
197
+
198
+
199
+ def get_content(word):
200
+ """
201
+ スクレイピングする。
202
+ :param word 検索キーワード
203
+ :return スクレイピング結果(HTML)とURL
204
+ ※ chromedriver.exe をCドライブ直下に置くこと。
205
+ """
206
+ driver = webdriver.Chrome(r'C:/chromedriver.exe')
207
+ driver.get("https://www.pmda.go.jp/PmdaSearch/kikiSearch/")
208
+ # id検索
209
+ elem_search_word = driver.find_element_by_id("txtName")
210
+ elem_search_word.send_keys(word)
211
+ # name検索
212
+ elem_search_btn = driver.find_element_by_name('btnA')
213
+ elem_search_btn.click()
214
+ change_window(driver)
215
+
216
+ # print(driver.page_source)
217
+ html = driver.page_source
218
+ cur_url = driver.current_url
219
+ return html, cur_url
220
+
221
+
222
+ def get_search_keyword():
223
+ """
224
+ エクセルファイルを開き、検索キーワードを取得する。
225
+ """
226
+ # テスト用
227
+ #yield "血液照射装置"
228
+ #yield "放射性医薬品合成設備"
229
+ from contextlib import closing
230
+ with closing(op.load_workbook('一般名称.xlsx')) as wb:
231
+ for i in range(1, 9):
232
+ ws = wb.active
233
+ yield ws['A' + str(i)].value
234
+
235
+
236
+ def parse(soup, cur_url: str):
237
+ """
238
+ スクレイピング結果を解析
239
+ """
240
+ for a_tag in soup.find_all('a'):
241
+ link_pdf = (urljoin(cur_url, a_tag.get('href')))
242
+ #print(link_pdf)
243
+ # link_PDFから文末がpdfと文中にPDFが入っているものを抽出
244
+ if (not link_pdf.lower().endswith('.pdf')) and ('/ResultDataSetPDF/' not in link_pdf):
245
+ continue
246
+ if 'searchhelp' not in link_pdf:
247
+ yield True, link_pdf
248
+
249
+
250
+ def output_excel(output:list, row_index: int):
251
+ """
252
+ エクセルに出力する。
253
+ :param output 行データ
254
+ :param row_index 出力するための開始行
255
+ """
256
+ #wb = op.load_workbook('URL_DATA.xlsx')
257
+ #ws = wb.active
258
+ print("#" * 50)
259
+ for i, (time_data, link_pdf, word_col) in enumerate(output, start=row_index):
260
+ print(i , time_data, link_pdf,word_col)
261
+ # ここにエクセルの設定処理を
262
+
263
+ #wb.save('URL_DATA.xlsx')
264
+
265
+ def main():
266
+ START_ROW = 0
267
+ row_index = 1
268
+ for word in get_search_keyword():
269
+ html, cur_url = get_content(word)
270
+ soup = BeautifulSoup(html, 'html.parser')
271
+ output = []
272
+ time_data = datetime.datetime.today()
273
+ for i, (has_pdf_link, link_pdf) in enumerate(parse(soup, cur_url), start=START_ROW):
274
+ word_col = ""
275
+ if i == START_ROW:
276
+ word_col = word
277
+ output.append([time_data, link_pdf, word_col])
278
+
279
+ output_excel(output, row_index)
280
+ row_index += len(output)
281
+
282
+
283
+ if __name__ == "__main__":
284
+ main()
162
285
  ```

8

追記

2018/07/23 11:29

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -76,4 +76,87 @@
76
76
  もしくはウィンドウズ環境にしかない`xlsx`ではなく汎用性のあるデータ構造csv形式など。
77
77
 
78
78
  この質問は**リストに質問文の画像のような形で値を格納したい**とも言いかえれます。
79
- こちらなら環境を選ばないので回答が付きやすいかと。
79
+ こちらなら環境を選ばないので回答が付きやすいかと。
80
+
81
+ ---
82
+
83
+ ◇不具合
84
+ 2列目に`False`が出力される原因はこのコードですね。
85
+ `has_pdf_link`が`Bool`型で値が`False`が設定されています。
86
+ ```Python
87
+ if not has_pdf_link:
88
+ print('False')
89
+ ws['B'+str(i)].value = has_pdf_link
90
+
91
+ ```
92
+
93
+ 試しに`selenium`と`openpyxl`を使わないように出力はリストになるように書き換えてみました。
94
+ ```Python
95
+ # -*- coding: utf-8 -*-
96
+ from bs4 import BeautifulSoup
97
+ from urllib.parse import urljoin
98
+ import datetime
99
+ import time
100
+ #import openpyxl as op
101
+
102
+
103
+ def get_content(word: str):
104
+ return """
105
+ <table class="SearchResultTable" id="ResultList">
106
+ <tbody><tr>
107
+ <th scope="col" style="width:13em" nowrap="">一般的名称</th>
108
+ <th scope="col" style="width:15em" nowrap="">販売名</th>
109
+ <th scope="col" style="width:15em" nowrap="">製造販売業者等</th>
110
+ <th scope="col" style="width:13em" nowrap="">添付文書</th>
111
+ <th scope="col" style="width:13em" nowrap="">改訂指示<br />反映履歴</th>
112
+ <th scope="col" style="width:13em" nowrap="">審査報告書/<br />再審査報告書等</th>
113
+ <th scope="col" style="width:13em" nowrap="">緊急安全性情報</th>
114
+ </tr>
115
+ <tr class="TrColor01">
116
+ <td><div><a target="_blank" href="/PmdaSearch/kikiDetail/GeneralList/20500BZZ00241000_A_01">血液照射装置</a></div></td>
117
+ <td><div>日立X線照射装置 MBR−1520A−TW</div></td>
118
+ <td><div>製造販売/株式会社 日立メディコ</div></td>
119
+ <td><div><a href="javascript:void(0)" onclick="detailDisp(&quot;PmdaSearch&quot; ,&quot;650053_20500BZZ00241000_A_01_01&quot;);">HTML</a><br /><a target="_blank" href="/PmdaSearch/kikiDetail/ResultDataSetPDF/650053_20500BZZ00241000_A_01_01">PDF (2007年12月19日)</a></div></td>
120
+ <td></td>
121
+ <td></td>
122
+ <td></td>
123
+ </tr>
124
+ </tbody></table>
125
+ """, "https://www.pmda.go.jp/PmdaSearch/kikiSearch/"
126
+
127
+
128
+ def get_search_keyword():
129
+ # テスト用
130
+ yield "血液照射装置"
131
+ yield "放射性医薬品合成設備"
132
+
133
+
134
+ def parse(soup, cur_url: str):
135
+ """
136
+ スクレイピング結果を解析
137
+ """
138
+ for a_tag in soup.find_all('a'):
139
+ link_pdf = (urljoin(cur_url, a_tag.get('href')))
140
+ # link_PDFから文末がpdfと文中にPDFが入っているものを抽出
141
+ if (not link_pdf.lower().endswith('.pdf')) and ('/ResultDataSetPDF/' not in link_pdf):
142
+ continue
143
+ if 'searchhelp' not in link_pdf:
144
+ yield True, link_pdf
145
+
146
+
147
+ def main():
148
+ for i, word in enumerate(get_search_keyword(), start=1):
149
+ html, cur_url = get_content(word)
150
+ soup = BeautifulSoup(html, 'html.parser')
151
+ output = []
152
+ time_data = datetime.datetime.today()
153
+ for has_pdf_link, link_pdf in parse(soup, cur_url):
154
+ output.append([time_data, link_pdf, word])
155
+ print(link_pdf)
156
+
157
+ print(output)
158
+
159
+
160
+ if __name__ == "__main__":
161
+ main()
162
+ ```

7

変更!

2018/07/23 10:42

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -2,9 +2,9 @@
2
2
  `一般名称.xlsx`の行を取得するための`index`と
3
3
  `URL_DATA.xlsx`に出力するための行番号(`index`)で使い回しを行っているからかと。
4
4
  別の変数としてください。
5
- 行単位に出力するならば、listにtupleで格納するのも良いのではないかと
5
+ 行単位に出力するならば、listにtupleで格納するのも良いのでは。
6
6
 
7
- 2, 日付が各行に出力されない原因は以下のインデントが一段深いからかと
7
+ 2, 日付が各行に出力されない原因は以下のインデントが一段深いです
8
8
  ```Python
9
9
  time.sleep(2)
10
10
  time_data = datetime.datetime.today()
@@ -12,13 +12,6 @@
12
12
  ws['A'+str(i)].value = time_data
13
13
  ```
14
14
 
15
- 3, 無条件にエクセルに出力を行っています。
16
- ```Python
17
- wb = op.load_workbook('URL_DATA.xlsx')
18
- ws = wb.active
19
- ws['C'+str(i)].value = word # ←この箇所
20
-
21
- ```
22
15
  ---
23
16
  追記・修正依頼欄に書ききれないので。
24
17
 

6

追記

2018/07/23 08:56

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -80,10 +80,7 @@
80
80
  ---
81
81
  b, 次に`一般名称.xlsx`や`URL_DATA.xlsx`のファイルが回答者の環境には無いため実行再現しずらいです。
82
82
  適当なサンプルデータを質問文に追記していただくか。
83
+ もしくはウィンドウズ環境にしかない`xlsx`ではなく汎用性のあるデータ構造csv形式など。
83
84
 
84
- もしくはウィンドウズ環境にしかない`xlsx`ではなく汎用性のあるデータ構造
85
-
86
- 特にxlsxはので余計に回答が付きづらくなります。
87
-
88
85
  この質問は**リストに質問文の画像のような形で値を格納したい**とも言いかえれます。
89
86
  こちらなら環境を選ばないので回答が付きやすいかと。

5

追記

2018/07/23 08:51

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -1,8 +1,9 @@
1
1
  1, デバックしていませんがコードを見て原因は`for i in range(1,9):`だと思います。
2
2
  `一般名称.xlsx`の行を取得するための`index`と
3
- `URL_DATA.xlsx`に出力するための行番号(`index`)で使い回しをているためかと。
3
+ `URL_DATA.xlsx`に出力するための行番号(`index`)で使い回しを行っているからかと。
4
- 別の変数とするか、`一般名称.xlsx`はこのループでは処理せずに、もう1つループを作ってリストに値を格納する形にしてください。そして`for i in range(1,9):`のループはリストを回せばよいでしょう。
4
+ 別の変数としてください。
5
- もしくは回答文の下の方で別関数(`get_search_keyword`)にするコード変更したのこれでも良いかと。
5
+ 行単位出力するならば、listtuple格納するのも良いのではないかと。
6
+
6
7
  2, 日付が各行に出力されない原因は以下のインデントが一段深いからかと。
7
8
  ```Python
8
9
  time.sleep(2)
@@ -10,6 +11,14 @@
10
11
 
11
12
  ws['A'+str(i)].value = time_data
12
13
  ```
14
+
15
+ 3, 無条件にエクセルに出力を行っています。
16
+ ```Python
17
+ wb = op.load_workbook('URL_DATA.xlsx')
18
+ ws = wb.active
19
+ ws['C'+str(i)].value = word # ←この箇所
20
+
21
+ ```
13
22
  ---
14
23
  追記・修正依頼欄に書ききれないので。
15
24
 
@@ -41,7 +50,9 @@
41
50
  change_window(driver)
42
51
 
43
52
  # print(driver.page_source)
53
+ html = driver.page_source
44
- return driver.page_source, driver.current_url
54
+ cur_url = driver.current_url
55
+ return html, cur_url
45
56
 
46
57
  ```
47
58
 

4

追記

2018/07/23 08:50

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -2,7 +2,7 @@
2
2
  `一般名称.xlsx`の行を取得するための`index`と
3
3
  `URL_DATA.xlsx`に出力するための行番号(`index`)で使い回しをしているためかと。
4
4
  別の変数とするか、`一般名称.xlsx`はこのループでは処理せずに、もう1つループを作ってリストに値を格納する形にしてください。そして`for i in range(1,9):`のループはリストを回せばよいでしょう。
5
-
5
+ もしくは回答文の下の方で別関数(`get_search_keyword`)にするコードに変更したのでこれでも良いかと。
6
6
  2, 日付が各行に出力されない原因は以下のインデントが一段深いからかと。
7
7
  ```Python
8
8
  time.sleep(2)

3

変更!

2018/07/23 08:37

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -48,6 +48,24 @@
48
48
  ```Python
49
49
  html, cur_url = get_content(word)
50
50
  ```
51
+ 一般名称.xlsxから検索キーワードを取得する部分は以下のように(未テスト)
52
+ ```Python
53
+ def get_search_keyword():
54
+ """
55
+ エクセルファイルを開き、検索キーワードを取得する。
56
+ """
57
+ # テスト用
58
+ #yield "血液照射装置"
59
+ #yield "放射性医薬品合成設備"
60
+ from contextlib import closing
61
+ with closing(op.load_workbook('一般名称.xlsx')) as wb:
62
+ for i in range(1, 9):
63
+ ws = wb.active
64
+ yield ws['A' + str(i)].value
65
+ ```
66
+ ```Python
67
+ for i, word in enumerate(get_search_keyword(), start=1):
68
+ ```
51
69
  ---
52
70
  b, 次に`一般名称.xlsx`や`URL_DATA.xlsx`のファイルが回答者の環境には無いため実行再現しずらいです。
53
71
  適当なサンプルデータを質問文に追記していただくか。

2

補足

2018/07/23 08:34

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -1,12 +1,19 @@
1
- デバックしていませんがコードを見て原因は`for i in range(1,9):`だと思います。
1
+ 1, デバックしていませんがコードを見て原因は`for i in range(1,9):`だと思います。
2
2
  `一般名称.xlsx`の行を取得するための`index`と
3
3
  `URL_DATA.xlsx`に出力するための行番号(`index`)で使い回しをしているためかと。
4
4
  別の変数とするか、`一般名称.xlsx`はこのループでは処理せずに、もう1つループを作ってリストに値を格納する形にしてください。そして`for i in range(1,9):`のループはリストを回せばよいでしょう。
5
5
 
6
+ 2, 日付が各行に出力されない原因は以下のインデントが一段深いからかと。
7
+ ```Python
8
+ time.sleep(2)
9
+ time_data = datetime.datetime.today()
10
+
11
+ ws['A'+str(i)].value = time_data
12
+ ```
6
13
  ---
7
14
  追記・修正依頼欄に書ききれないので。
8
15
 
9
- 0. 初心者の方に多いのですが、質問者さんのように処理を一つの関数にどんどん追加していく人が多いです。
16
+ a. 初心者の方に多いのですが、質問者さんのように処理を一つの関数にどんどん追加していく人が多いです。
10
17
  これをしてしまうとなんらかの問題がソースコードに発生したときに、
11
18
  どこの処理が問題なのかの原因の切り分けが不可能になりやすいです。
12
19
  今回の件は出力の問題なのでスクレイピングは**ほぼ**関係ないですよね。
@@ -42,7 +49,7 @@
42
49
  html, cur_url = get_content(word)
43
50
  ```
44
51
  ---
45
- 2, 次に`一般名称.xlsx`や`URL_DATA.xlsx`のファイルが回答者の環境には無いため実行再現しずらいです。
52
+ b, 次に`一般名称.xlsx`や`URL_DATA.xlsx`のファイルが回答者の環境には無いため実行再現しずらいです。
46
53
  適当なサンプルデータを質問文に追記していただくか。
47
54
 
48
55
  もしくはウィンドウズ環境にしかない`xlsx`ではなく汎用性のあるデータ構造

1

追記

2018/07/23 08:17

投稿

umyu
umyu

スコア5846

answer CHANGED
@@ -8,10 +8,12 @@
8
8
 
9
9
  0. 初心者の方に多いのですが、質問者さんのように処理を一つの関数にどんどん追加していく人が多いです。
10
10
  これをしてしまうとなんらかの問題がソースコードに発生したときに、
11
- どこの処理が原因なのかの原因の切り分けが不可能になりやすいです。
11
+ どこの処理が問題なのかの原因の切り分けが不可能になりやすいです。
12
+ 今回の件は出力の問題なのでスクレイピングは**ほぼ**関係ないですよね。
13
+ でも同じ関数内に書いてしまうと**ほぼ**なのでもしかしたら関係あるかもで調査する必要があります。
12
14
  対策としては適度な関数分割です。
13
15
 
14
- スクレイピングをして、HTMLを取得する部分に関してこのようにできます。
16
+ スクレイピングをして、HTMLを取得する部分のコードは以下のようにできます。
15
17
  こうすることでスクレイピングの処理は関数内で閉じているので、該当の処理は意識しなくても良くなります。
16
18
  ```Python
17
19
  def get_content(word):
@@ -47,5 +49,5 @@
47
49
 
48
50
  特にxlsxはので余計に回答が付きづらくなります。
49
51
 
50
- この質問は**検索キーワードを元にリストに値を格納したい**とも言いかえれます。
52
+ この質問は**リストに質問文の画像のような形で値を格納したい**とも言いかえれます。
51
53
  こちらなら環境を選ばないので回答が付きやすいかと。