如何正确解析网页链接以避免使用 Wget 时出现 403 错误?
我昨天才开始学习 Python,编码技能非常有限。我正在尝试编写一个 Python 脚本来处理一个 PDF 文件夹。每个 PDF 至少包含 1 个,最多可能包含 15 个或更多指向补充文档的 Web 链接。我认为我有一个好的开始,但在尝试使用 wget 函数时,我不断遇到“HTTP 错误 403:禁止”错误。我相信我只是没有正确解析 Web 链接。我认为主要问题在于,因为网页链接大多是超长的“s3.amazonaws.com”链接。
供参考:
尝试在我的代码中解析链接后显示该链接(不起作用,尝试下载时显示“未知的 URL 类型”): https%3A//s3.amazonaws.com/os_uploads/2169504_DFA%2520train%2520pass.PNG%3FAWSAccessKeyId%3DAKIAIPCTK7BDMEW7SP4Q%26Expires%3D1909634500%26Signature%3DaQlQXVR8UuYLtkzjvcKJ5tiVrZQ%253D%26response-content-disposition%3Dattachment%253B%2520filename%252A%253Dutf-8%2527%2527DFA%252520train%252520pass.PNG
此外,如果有人想评论我如何以愚蠢的方式做到这一点。每个 PDF 都以 6 位数字的字符串开头,一旦我下载了补充文档,我就想自动保存并将它们命名为 XXXXXX_attachY。*其中 X 是识别数字字符串,Y 只会随着每个附件而增加。我还没有让我的代码运行到足以测试这一点,但我相当肯定我也不正确。
救命!
#!/usr/bin/env python3
import os
import glob
import pdfx
import wget
import urllib.parse
## Accessing and Creating Six Digit File Code
pdf_dir = "/users/USERNAME/desktop/worky"
pdf_files = glob.glob("%s/*.pdf" % pdf_dir)
for file in pdf_files:
## Identify File Name and Limit to Digits
filename = os.path.basename(file)
newname = filename[0:6]
## Run PDFX to identify and download links
pdf = pdfx.PDFx(filename)
url_list = pdf.get_references_as_dict()
attachment_counter = (1)
for x in url_list["url"]:
if x[0:4] == "http":
parsed_url = urllib.parse.quote(x, safe='://')
print (parsed_url)
wget.download(parsed_url, '/users/USERNAME/desktop/worky/(newname)_attach(attachment_counter).*')
##os.rename(r'/users/USERNAME/desktop/worky/(filename).*',r'/users/USERNAME/desktop/worky/(newname)_attach(attachment_counter).*')
attachment_counter += 1
for x in url_list["pdf"]:
print (parsed_url + "\n")```
尝试在线抓取文本或文件时,我更喜欢使用
requests
(
https://requests.readthedocs.io/en/master/
)。我快速尝试了一下
wget
,但还是出现了同样的错误(可能与
wget
使用的用户代理 HTTP 标头有关)。
-
wget
和 HTTP 标头问题: 使用 python urllib 从 url 下载图像,但收到 HTTP 错误 403:禁止 - HTTP 标头: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
requests
的优点在于它允许您按您想要的方式修改 HTTP 标头 (
https://requests.readthedocs.io/en/master/user/quickstart/#custom-headers
)。
import requests
r = requests.get("https://s3.amazonaws.com/os_uploads/2169504_DFA%20train%20pass.PNG?AWSAccessKeyId=AKIAIPCTK7BDMEW7SP4Q&Expires=1909634500&Signature=aQlQXVR8UuYLtkzjvcKJ5tiVrZQ=&response-content-disposition=attachment;%20filename*=utf-8''DFA%2520train%2520pass.PNG")
with open("myfile.png", "wb") as file:
file.write(r.content)
我不确定我是否理解您要做什么,但也许您想使用格式化的字符串来构建您的 URL ( https://docs.python.org/3/library/stdtypes.html?highlight=format#str.format )?
也许在您的情况下检查字符串索引是可以的 (
if x[0:4] == "http":
),但我认为您应该检查 python
re
包以使用正则表达式来捕获您想要的元素在文档中 (
https://docs.python.org/3/library/re.html
)。
import re
regex = re.compile(r"^http://")
if re.match(regex, mydocument):
<do something>
此行为的原因在于 wget 库内部。它使用
urllib.parse.quote()
(
https://docs.python.org/3/library/urllib.parse.html#urllib.parse.quote
) 对 URL 进行编码。
基本上,它会用相应的
%xx
转义字符替换字符。您的 URL 已转义,但库不知道这一点。当它解析
%20
时,它将
%
视为需要替换的字符,因此结果为
%2520
和不同的 URL - 因此出现 403 错误。
您可以先解码该 URL,然后传递它,但这样一来,您将遇到此库的另一个问题,因为您的 URL 具有参数
filename*=
,但该库需要
filename=
。
我建议做这样的事情:
# get the file
req = requests.get(parsed_url)
# parse your URL to get GET parameters
get_parameters = [x for x in parsed_url.split('?')[1].split('&')]
filename = ''
# find the get parameter with the name
for get_parameter in get_parameters:
if "filename*=" in get_parameter:
# split it to get the name
filename = get_parameter.split('filename*=')[1]
# save the file
with open(<path> + filename, 'wb') as file:
file.write(req.content)
我还建议删除该文件名中的
utf-8''
,因为我认为它实际上不是文件名的一部分。您也可以使用正则表达式来获取文件名,但这对我来说更容易。