質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.20%

【Python,スクレイピング】HPからPDFファイルを保存する方法について

解決済

回答 1

投稿

  • 評価
  • クリップ 2
  • VIEW 1,060

yusyte

score 11

前提・実現したいこと

経産省のHPから、PDFファイルを保存したい。

発生している問題・エラーメッセージ

[vagrant@localhost 20190627_scrayping_warkinggroup]$ python warkinggroup.py
Traceback (most recent call last):
File "warkinggroup.py", line 22, in <module>
urllib.request.urlretrieve(url_1, filepath)
File "/home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/urllib/request.py", line 188, in urlretrieve
with contextlib.closing(urlopen(url, data)) as fp:
File "/home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/urllib/request.py", line 163, in urlopen
return opener.open(url, data, timeout)
File "/home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/urllib/request.py", line 472, in open
response = meth(req, response)
File "/home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/urllib/request.py", line 582, in http_response
'http', request, response, code, msg, hdrs)
File "/home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/urllib/request.py", line 510, in error
return self._call_chain(*args)
File "/home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/urllib/request.py", line 444, in _call_chain
result = func(*args)
File "/home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/urllib/request.py", line 590, in http_error_default
raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 403: Forbidden

該当のソースコード

import requests
from bs4 import BeautifulSoup
import urllib.request
from urllib.parse import urljoin
import re
import time

url = "https://www.meti.go.jp/shingikai/sankoshin/hoan_shohi/denryoku_anzen/019.html"
base = "https://www.meti.go.jp/shingikai/sankoshin/hoan_shohi/denryoku_anzen/"
html = requests.get(url)
soup = BeautifulSoup(html.content, "html.parser")

div = soup.find("div", class_= "main w1000")
li = div.find_next("li")
tags = li.find_all_next("a")

for i in range(len(tags)):
    filepath = "{}.pdf".format(i)
    target = tags[i]["href"]
    if re.match(r"pdf", target):
        url_1 = urljoin(base, target)
        urllib.request.urlretrieve(url_1, filepath)
        # print(target)
        # print(url_1)
        # time.sleep(1)

試したこと

最後のurllib.request.urlretrieve(url_1, filepath)をコメントアウトとし、

print(target) # print(url_1) をアンコメントするとしっかりprintされます。

baseの絶対パス表示がおかしいのかと考え、様々試しましたがなかなかうまくいきません。
また、time関数で相手のサーバーに負荷をかけないよう配慮して行ってもいかなかったので、
やはりurllib.request.urlretrieve(url_1, filepath)の部分もしくはその変数が間違いであるんだと思います。

補足情報(FW/ツールのバージョンなど)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

結論

以下のように書き換えることで実行できました。

import requests
from bs4 import BeautifulSoup
import urllib.request
from urllib.parse import urljoin
import re
import time

url = "https://www.meti.go.jp/shingikai/sankoshin/hoan_shohi/denryoku_anzen/019.html"
base = "https://www.meti.go.jp/shingikai/sankoshin/hoan_shohi/denryoku_anzen/"
html = requests.get(url)
soup = BeautifulSoup(html.content, "html.parser")

div = soup.find("div", class_= "main w1000")
li = div.find_next("li")
tags = li.find_all_next("a")
print(tags)

headers = {"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0"}

for i in range(len(tags)):
    filepath = "{}.pdf".format(i)
    target = tags[i]["href"]
    if re.match(r"pdf", target):
        url_1 = urljoin(base, target)
        request = urllib.request.Request(url=url_1, headers=headers)
        with open(filepath, "wb") as f:
            f.write(urllib.request.urlopen(request).read())

diff

  import requests
  from bs4 import BeautifulSoup
  import urllib.request
  from urllib.parse import urljoin
  import re
  import time

  url = "https://www.meti.go.jp/shingikai/sankoshin/hoan_shohi/denryoku_anzen/019.html"
  base = "https://www.meti.go.jp/shingikai/sankoshin/hoan_shohi/denryoku_anzen/"
  html = requests.get(url)
  soup = BeautifulSoup(html.content, "html.parser")

  div = soup.find("div", class_= "main w1000")
  li = div.find_next("li")
  tags = li.find_all_next("a")

+ headers = {"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0"}

  for i in range(len(tags)):
      filepath = "{}.pdf".format(i)
      target = tags[i]["href"]
      if re.match(r"pdf", target):
          url_1 = urljoin(base, target)
+         request = urllib.request.Request(url=url_1, headers=headers)
+         with open(filepath, "wb") as f:
+             f.write(urllib.request.urlopen(request).read())
-         urllib.request.urlretrieve(url_1, filepath)
-         # print(target)
-         # print(url_1)
-         # time.sleep(1)

説明

リクエストの header に User-Agent が指定されていないことで403エラーが出ていました。
そこで、 header を指定するために urllib.request.urlretrieve ではなく urllib.request.urlopen を使うように変更し、開いたファイルを保存する処理を追加することで、正常に動作するようになると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/30 17:06

    ご回答いただきましてありがとうございます。
    ご指摘いただいた通り修正したところ、無事実行できました。
    r_takahama様の解説と、https://pc.atsuhiro-me.net/entry/2013/12/04/204505を参照して、
    大変理解が深まりました。
    user-Agentでアクセスを制限していることがあるのですね。。勉強になります。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.20%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る