回答編集履歴

10

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

2018/07/23 13:29

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -326,7 +326,7 @@
326
326
 
327
327
 
328
328
 
329
- 以下の`False`の仕様がよく分かりませんでしたので、その部分がうまく実装できてないと思いますが。
329
+ 以下の`False`の仕様がよく分かりませんでしたので、その部分がうまく実装できてないすが。
330
330
 
331
331
  質問文の画像を見る限りこういうふうに出力したいのでしょうか。
332
332
 
@@ -380,21 +380,35 @@
380
380
 
381
381
 
382
382
 
383
- def change_window(browser):
384
-
385
- all_handles = set(browser.window_handles)
386
-
387
- switch_to = all_handles - set([browser.current_window_handle])
388
-
389
- assert len(switch_to) == 1
390
-
391
- browser.switch_to.window(*switch_to)
392
-
393
-
394
-
395
-
396
-
397
- def get_content(word):
383
+ def get_search_keyword():
384
+
385
+ """
386
+
387
+ エクセルファイルを開き、検索キーワードを取得する。
388
+
389
+ """
390
+
391
+ # テスト用
392
+
393
+ #yield "血液照射装置"
394
+
395
+ #yield "放射性医薬品合成設備"
396
+
397
+ from contextlib import closing
398
+
399
+ with closing(op.load_workbook('一般名称.xlsx')) as wb:
400
+
401
+ for i in range(1, 9):
402
+
403
+ ws = wb.active
404
+
405
+ yield ws['A' + str(i)].value
406
+
407
+
408
+
409
+
410
+
411
+ def get_content(word: str) -> tuple:
398
412
 
399
413
  """
400
414
 
@@ -408,6 +422,24 @@
408
422
 
409
423
  """
410
424
 
425
+ def change_window(browser):
426
+
427
+ """
428
+
429
+ ブラウザのウィンドウを切り替える。
430
+
431
+ """
432
+
433
+ all_handles = set(browser.window_handles)
434
+
435
+ switch_to = all_handles - set([browser.current_window_handle])
436
+
437
+ assert len(switch_to) == 1
438
+
439
+ browser.switch_to.window(*switch_to)
440
+
441
+
442
+
411
443
  driver = webdriver.Chrome(r'C:/chromedriver.exe')
412
444
 
413
445
  driver.get("https://www.pmda.go.jp/PmdaSearch/kikiSearch/")
@@ -434,40 +466,16 @@
434
466
 
435
467
  cur_url = driver.current_url
436
468
 
469
+ driver.quit()
470
+
471
+
472
+
437
473
  return html, cur_url
438
474
 
439
475
 
440
476
 
441
477
 
442
478
 
443
- def get_search_keyword():
444
-
445
- """
446
-
447
- エクセルファイルを開き、検索キーワードを取得する。
448
-
449
- """
450
-
451
- # テスト用
452
-
453
- #yield "血液照射装置"
454
-
455
- #yield "放射性医薬品合成設備"
456
-
457
- from contextlib import closing
458
-
459
- with closing(op.load_workbook('一般名称.xlsx')) as wb:
460
-
461
- for i in range(1, 9):
462
-
463
- ws = wb.active
464
-
465
- yield ws['A' + str(i)].value
466
-
467
-
468
-
469
-
470
-
471
479
  def parse(soup, cur_url: str):
472
480
 
473
481
  """
@@ -526,6 +534,8 @@
526
534
 
527
535
 
528
536
 
537
+
538
+
529
539
  def main():
530
540
 
531
541
  START_ROW = 0
@@ -544,11 +554,7 @@
544
554
 
545
555
  for i, (has_pdf_link, link_pdf) in enumerate(parse(soup, cur_url), start=START_ROW):
546
556
 
547
- word_col = ""
557
+ word_col = word if i == START_ROW else ""
548
-
549
- if i == START_ROW:
550
-
551
- word_col = word
552
558
 
553
559
  output.append([time_data, link_pdf, word_col])
554
560
 
@@ -566,4 +572,6 @@
566
572
 
567
573
  main()
568
574
 
575
+
576
+
569
- ```
577
+ ```

9

補足

2018/07/23 13:29

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -321,3 +321,249 @@
321
321
  main()
322
322
 
323
323
  ```
324
+
325
+ ---
326
+
327
+
328
+
329
+ 以下の`False`の仕様がよく分かりませんでしたので、その部分がうまく実装できてないと思いますが。
330
+
331
+ 質問文の画像を見る限りこういうふうに出力したいのでしょうか。
332
+
333
+ ```Python
334
+
335
+ if not has_pdf_link:
336
+
337
+ print('False')
338
+
339
+ ws['B'+str(i)].value = has_pdf_link
340
+
341
+
342
+
343
+ time.sleep(2)
344
+
345
+ time_data = datetime.datetime.today()
346
+
347
+
348
+
349
+ ws['A'+str(i)].value = time_data
350
+
351
+ ```
352
+
353
+
354
+
355
+ ```Python
356
+
357
+ # -*- coding: utf-8 -*-
358
+
359
+ from selenium import webdriver
360
+
361
+ from selenium.webdriver.common.by import By
362
+
363
+ from selenium.webdriver.support.ui import WebDriverWait
364
+
365
+ from urllib import request
366
+
367
+ from bs4 import BeautifulSoup
368
+
369
+ import requests
370
+
371
+ from urllib.parse import urljoin
372
+
373
+ import openpyxl as op
374
+
375
+ import datetime
376
+
377
+ import time
378
+
379
+
380
+
381
+
382
+
383
+ def change_window(browser):
384
+
385
+ all_handles = set(browser.window_handles)
386
+
387
+ switch_to = all_handles - set([browser.current_window_handle])
388
+
389
+ assert len(switch_to) == 1
390
+
391
+ browser.switch_to.window(*switch_to)
392
+
393
+
394
+
395
+
396
+
397
+ def get_content(word):
398
+
399
+ """
400
+
401
+ スクレイピングする。
402
+
403
+ :param word 検索キーワード
404
+
405
+ :return スクレイピング結果(HTML)とURL
406
+
407
+ ※ chromedriver.exe をCドライブ直下に置くこと。
408
+
409
+ """
410
+
411
+ driver = webdriver.Chrome(r'C:/chromedriver.exe')
412
+
413
+ driver.get("https://www.pmda.go.jp/PmdaSearch/kikiSearch/")
414
+
415
+ # id検索
416
+
417
+ elem_search_word = driver.find_element_by_id("txtName")
418
+
419
+ elem_search_word.send_keys(word)
420
+
421
+ # name検索
422
+
423
+ elem_search_btn = driver.find_element_by_name('btnA')
424
+
425
+ elem_search_btn.click()
426
+
427
+ change_window(driver)
428
+
429
+
430
+
431
+ # print(driver.page_source)
432
+
433
+ html = driver.page_source
434
+
435
+ cur_url = driver.current_url
436
+
437
+ return html, cur_url
438
+
439
+
440
+
441
+
442
+
443
+ def get_search_keyword():
444
+
445
+ """
446
+
447
+ エクセルファイルを開き、検索キーワードを取得する。
448
+
449
+ """
450
+
451
+ # テスト用
452
+
453
+ #yield "血液照射装置"
454
+
455
+ #yield "放射性医薬品合成設備"
456
+
457
+ from contextlib import closing
458
+
459
+ with closing(op.load_workbook('一般名称.xlsx')) as wb:
460
+
461
+ for i in range(1, 9):
462
+
463
+ ws = wb.active
464
+
465
+ yield ws['A' + str(i)].value
466
+
467
+
468
+
469
+
470
+
471
+ def parse(soup, cur_url: str):
472
+
473
+ """
474
+
475
+ スクレイピング結果を解析
476
+
477
+ """
478
+
479
+ for a_tag in soup.find_all('a'):
480
+
481
+ link_pdf = (urljoin(cur_url, a_tag.get('href')))
482
+
483
+ #print(link_pdf)
484
+
485
+ # link_PDFから文末がpdfと文中にPDFが入っているものを抽出
486
+
487
+ if (not link_pdf.lower().endswith('.pdf')) and ('/ResultDataSetPDF/' not in link_pdf):
488
+
489
+ continue
490
+
491
+ if 'searchhelp' not in link_pdf:
492
+
493
+ yield True, link_pdf
494
+
495
+
496
+
497
+
498
+
499
+ def output_excel(output:list, row_index: int):
500
+
501
+ """
502
+
503
+ エクセルに出力する。
504
+
505
+ :param output 行データ
506
+
507
+ :param row_index 出力するための開始行
508
+
509
+ """
510
+
511
+ #wb = op.load_workbook('URL_DATA.xlsx')
512
+
513
+ #ws = wb.active
514
+
515
+ print("#" * 50)
516
+
517
+ for i, (time_data, link_pdf, word_col) in enumerate(output, start=row_index):
518
+
519
+ print(i , time_data, link_pdf,word_col)
520
+
521
+ # ここにエクセルの設定処理を
522
+
523
+
524
+
525
+ #wb.save('URL_DATA.xlsx')
526
+
527
+
528
+
529
+ def main():
530
+
531
+ START_ROW = 0
532
+
533
+ row_index = 1
534
+
535
+ for word in get_search_keyword():
536
+
537
+ html, cur_url = get_content(word)
538
+
539
+ soup = BeautifulSoup(html, 'html.parser')
540
+
541
+ output = []
542
+
543
+ time_data = datetime.datetime.today()
544
+
545
+ for i, (has_pdf_link, link_pdf) in enumerate(parse(soup, cur_url), start=START_ROW):
546
+
547
+ word_col = ""
548
+
549
+ if i == START_ROW:
550
+
551
+ word_col = word
552
+
553
+ output.append([time_data, link_pdf, word_col])
554
+
555
+
556
+
557
+ output_excel(output, row_index)
558
+
559
+ row_index += len(output)
560
+
561
+
562
+
563
+
564
+
565
+ if __name__ == "__main__":
566
+
567
+ main()
568
+
569
+ ```

8

追記

2018/07/23 11:29

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -155,3 +155,169 @@
155
155
  この質問は**リストに質問文の画像のような形で値を格納したい**とも言いかえれます。
156
156
 
157
157
  こちらなら環境を選ばないので回答が付きやすいかと。
158
+
159
+
160
+
161
+ ---
162
+
163
+
164
+
165
+ ◇不具合
166
+
167
+ 2列目に`False`が出力される原因はこのコードですね。
168
+
169
+ `has_pdf_link`が`Bool`型で値が`False`が設定されています。
170
+
171
+ ```Python
172
+
173
+ if not has_pdf_link:
174
+
175
+ print('False')
176
+
177
+ ws['B'+str(i)].value = has_pdf_link
178
+
179
+
180
+
181
+ ```
182
+
183
+
184
+
185
+ 試しに`selenium`と`openpyxl`を使わないように出力はリストになるように書き換えてみました。
186
+
187
+ ```Python
188
+
189
+ # -*- coding: utf-8 -*-
190
+
191
+ from bs4 import BeautifulSoup
192
+
193
+ from urllib.parse import urljoin
194
+
195
+ import datetime
196
+
197
+ import time
198
+
199
+ #import openpyxl as op
200
+
201
+
202
+
203
+
204
+
205
+ def get_content(word: str):
206
+
207
+ return """
208
+
209
+ <table class="SearchResultTable" id="ResultList">
210
+
211
+ <tbody><tr>
212
+
213
+ <th scope="col" style="width:13em" nowrap="">一般的名称</th>
214
+
215
+ <th scope="col" style="width:15em" nowrap="">販売名</th>
216
+
217
+ <th scope="col" style="width:15em" nowrap="">製造販売業者等</th>
218
+
219
+ <th scope="col" style="width:13em" nowrap="">添付文書</th>
220
+
221
+ <th scope="col" style="width:13em" nowrap="">改訂指示<br />反映履歴</th>
222
+
223
+ <th scope="col" style="width:13em" nowrap="">審査報告書/<br />再審査報告書等</th>
224
+
225
+ <th scope="col" style="width:13em" nowrap="">緊急安全性情報</th>
226
+
227
+ </tr>
228
+
229
+ <tr class="TrColor01">
230
+
231
+ <td><div><a target="_blank" href="/PmdaSearch/kikiDetail/GeneralList/20500BZZ00241000_A_01">血液照射装置</a></div></td>
232
+
233
+ <td><div>日立X線照射装置 MBR−1520A−TW</div></td>
234
+
235
+ <td><div>製造販売/株式会社 日立メディコ</div></td>
236
+
237
+ <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>
238
+
239
+ <td></td>
240
+
241
+ <td></td>
242
+
243
+ <td></td>
244
+
245
+ </tr>
246
+
247
+ </tbody></table>
248
+
249
+ """, "https://www.pmda.go.jp/PmdaSearch/kikiSearch/"
250
+
251
+
252
+
253
+
254
+
255
+ def get_search_keyword():
256
+
257
+ # テスト用
258
+
259
+ yield "血液照射装置"
260
+
261
+ yield "放射性医薬品合成設備"
262
+
263
+
264
+
265
+
266
+
267
+ def parse(soup, cur_url: str):
268
+
269
+ """
270
+
271
+ スクレイピング結果を解析
272
+
273
+ """
274
+
275
+ for a_tag in soup.find_all('a'):
276
+
277
+ link_pdf = (urljoin(cur_url, a_tag.get('href')))
278
+
279
+ # link_PDFから文末がpdfと文中にPDFが入っているものを抽出
280
+
281
+ if (not link_pdf.lower().endswith('.pdf')) and ('/ResultDataSetPDF/' not in link_pdf):
282
+
283
+ continue
284
+
285
+ if 'searchhelp' not in link_pdf:
286
+
287
+ yield True, link_pdf
288
+
289
+
290
+
291
+
292
+
293
+ def main():
294
+
295
+ for i, word in enumerate(get_search_keyword(), start=1):
296
+
297
+ html, cur_url = get_content(word)
298
+
299
+ soup = BeautifulSoup(html, 'html.parser')
300
+
301
+ output = []
302
+
303
+ time_data = datetime.datetime.today()
304
+
305
+ for has_pdf_link, link_pdf in parse(soup, cur_url):
306
+
307
+ output.append([time_data, link_pdf, word])
308
+
309
+ print(link_pdf)
310
+
311
+
312
+
313
+ print(output)
314
+
315
+
316
+
317
+
318
+
319
+ if __name__ == "__main__":
320
+
321
+ main()
322
+
323
+ ```

7

変更!

2018/07/23 10:42

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -6,11 +6,11 @@
6
6
 
7
7
  別の変数としてください。
8
8
 
9
- 行単位に出力するならば、listにtupleで格納するのも良いのではないかと
9
+ 行単位に出力するならば、listにtupleで格納するのも良いのでは。
10
10
 
11
11
 
12
12
 
13
- 2, 日付が各行に出力されない原因は以下のインデントが一段深いからかと
13
+ 2, 日付が各行に出力されない原因は以下のインデントが一段深いです
14
14
 
15
15
  ```Python
16
16
 
@@ -25,20 +25,6 @@
25
25
  ```
26
26
 
27
27
 
28
-
29
- 3, 無条件にエクセルに出力を行っています。
30
-
31
- ```Python
32
-
33
- wb = op.load_workbook('URL_DATA.xlsx')
34
-
35
- ws = wb.active
36
-
37
- ws['C'+str(i)].value = word # ←この箇所
38
-
39
-
40
-
41
- ```
42
28
 
43
29
  ---
44
30
 

6

追記

2018/07/23 08:56

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -162,13 +162,7 @@
162
162
 
163
163
  適当なサンプルデータを質問文に追記していただくか。
164
164
 
165
-
166
-
167
- もしくはウィンドウズ環境にしかない`xlsx`ではなく汎用性のあるデータ構造
165
+ もしくはウィンドウズ環境にしかない`xlsx`ではなく汎用性のあるデータ構造csv形式など。
168
-
169
-
170
-
171
- 特にxlsxはので余計に回答が付きづらくなります。
172
166
 
173
167
 
174
168
 

5

追記

2018/07/23 08:51

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -2,11 +2,13 @@
2
2
 
3
3
  `一般名称.xlsx`の行を取得するための`index`と
4
4
 
5
- `URL_DATA.xlsx`に出力するための行番号(`index`)で使い回しをているためかと。
5
+ `URL_DATA.xlsx`に出力するための行番号(`index`)で使い回しを行っているからかと。
6
6
 
7
- 別の変数とするか、`一般名称.xlsx`はこのループでは処理せずに、もう1つループを作ってリストに値を格納する形にしてください。そして`for i in range(1,9):`のループはリストを回せばよいでしょう。
7
+ 別の変数としてください。
8
8
 
9
- もしくは回答文の下の方で別関数(`get_search_keyword`)にするコードに変更したでこれでも良いかと。
9
+ 行単位に出力するならば、listにtupleで格納するのも良いのではないかと。
10
+
11
+
10
12
 
11
13
  2, 日付が各行に出力されない原因は以下のインデントが一段深いからかと。
12
14
 
@@ -19,6 +21,22 @@
19
21
 
20
22
 
21
23
  ws['A'+str(i)].value = time_data
24
+
25
+ ```
26
+
27
+
28
+
29
+ 3, 無条件にエクセルに出力を行っています。
30
+
31
+ ```Python
32
+
33
+ wb = op.load_workbook('URL_DATA.xlsx')
34
+
35
+ ws = wb.active
36
+
37
+ ws['C'+str(i)].value = word # ←この箇所
38
+
39
+
22
40
 
23
41
  ```
24
42
 
@@ -84,7 +102,11 @@
84
102
 
85
103
  # print(driver.page_source)
86
104
 
105
+ html = driver.page_source
106
+
87
- return driver.page_source, driver.current_url
107
+ cur_url = driver.current_url
108
+
109
+ return html, cur_url
88
110
 
89
111
 
90
112
 

4

追記

2018/07/23 08:50

投稿

umyu
umyu

スコア5846

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

3

変更!

2018/07/23 08:37

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -98,6 +98,42 @@
98
98
 
99
99
  ```
100
100
 
101
+ 一般名称.xlsxから検索キーワードを取得する部分は以下のように(未テスト)
102
+
103
+ ```Python
104
+
105
+ def get_search_keyword():
106
+
107
+ """
108
+
109
+ エクセルファイルを開き、検索キーワードを取得する。
110
+
111
+ """
112
+
113
+ # テスト用
114
+
115
+ #yield "血液照射装置"
116
+
117
+ #yield "放射性医薬品合成設備"
118
+
119
+ from contextlib import closing
120
+
121
+ with closing(op.load_workbook('一般名称.xlsx')) as wb:
122
+
123
+ for i in range(1, 9):
124
+
125
+ ws = wb.active
126
+
127
+ yield ws['A' + str(i)].value
128
+
129
+ ```
130
+
131
+ ```Python
132
+
133
+ for i, word in enumerate(get_search_keyword(), start=1):
134
+
135
+ ```
136
+
101
137
  ---
102
138
 
103
139
  b, 次に`一般名称.xlsx`や`URL_DATA.xlsx`のファイルが回答者の環境には無いため実行再現しずらいです。

2

補足

2018/07/23 08:34

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -1,4 +1,4 @@
1
- デバックしていませんがコードを見て原因は`for i in range(1,9):`だと思います。
1
+ 1, デバックしていませんがコードを見て原因は`for i in range(1,9):`だと思います。
2
2
 
3
3
  `一般名称.xlsx`の行を取得するための`index`と
4
4
 
@@ -8,13 +8,27 @@
8
8
 
9
9
 
10
10
 
11
+ 2, 日付が各行に出力されない原因は以下のインデントが一段深いからかと。
12
+
13
+ ```Python
14
+
15
+ time.sleep(2)
16
+
17
+ time_data = datetime.datetime.today()
18
+
19
+
20
+
21
+ ws['A'+str(i)].value = time_data
22
+
23
+ ```
24
+
11
25
  ---
12
26
 
13
27
  追記・修正依頼欄に書ききれないので。
14
28
 
15
29
 
16
30
 
17
- 0. 初心者の方に多いのですが、質問者さんのように処理を一つの関数にどんどん追加していく人が多いです。
31
+ a. 初心者の方に多いのですが、質問者さんのように処理を一つの関数にどんどん追加していく人が多いです。
18
32
 
19
33
  これをしてしまうとなんらかの問題がソースコードに発生したときに、
20
34
 
@@ -86,7 +100,7 @@
86
100
 
87
101
  ---
88
102
 
89
- 2, 次に`一般名称.xlsx`や`URL_DATA.xlsx`のファイルが回答者の環境には無いため実行再現しずらいです。
103
+ b, 次に`一般名称.xlsx`や`URL_DATA.xlsx`のファイルが回答者の環境には無いため実行再現しずらいです。
90
104
 
91
105
  適当なサンプルデータを質問文に追記していただくか。
92
106
 

1

追記

2018/07/23 08:17

投稿

umyu
umyu

スコア5846

test CHANGED
@@ -18,13 +18,17 @@
18
18
 
19
19
  これをしてしまうとなんらかの問題がソースコードに発生したときに、
20
20
 
21
- どこの処理が原因なのかの原因の切り分けが不可能になりやすいです。
21
+ どこの処理が問題なのかの原因の切り分けが不可能になりやすいです。
22
+
23
+ 今回の件は出力の問題なのでスクレイピングは**ほぼ**関係ないですよね。
24
+
25
+ でも同じ関数内に書いてしまうと**ほぼ**なのでもしかしたら関係あるかもで調査する必要があります。
22
26
 
23
27
  対策としては適度な関数分割です。
24
28
 
25
29
 
26
30
 
27
- スクレイピングをして、HTMLを取得する部分に関してこのようにできます。
31
+ スクレイピングをして、HTMLを取得する部分のコードは以下のようにできます。
28
32
 
29
33
  こうすることでスクレイピングの処理は関数内で閉じているので、該当の処理は意識しなくても良くなります。
30
34
 
@@ -96,6 +100,6 @@
96
100
 
97
101
 
98
102
 
99
- この質問は**検索キーワードを元にリストに値を格納したい**とも言いかえれます。
103
+ この質問は**リストに質問文の画像のような形で値を格納したい**とも言いかえれます。
100
104
 
101
105
  こちらなら環境を選ばないので回答が付きやすいかと。