开发者问题收集

如何正确解析网页链接以避免使用 Wget 时出现 403 错误?

2020-07-17
621

我昨天才开始学习 Python,编码技能非常有限。我正在尝试编写一个 Python 脚本来处理一个 PDF 文件夹。每个 PDF 至少包含 1 个,最多可能包含 15 个或更多指向补充文档的 Web 链接。我认为我有一个好的开始,但在尝试使用 wget 函数时,我不断遇到“HTTP 错误 403:禁止”错误。我相信我只是没有正确解析 Web 链接。我认为主要问题在于,因为网页链接大多是超长的“s3.amazonaws.com”链接。

供参考:

直接从 PDF 复制的链接(可下载): 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

尝试在我的代码中解析链接后显示该链接(不起作用,尝试下载时显示“未知的 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")```
2个回答

尝试在线抓取文本或文件时,我更喜欢使用 requests ( https://requests.readthedocs.io/en/master/ )。我快速尝试了一下 wget ,但还是出现了同样的错误(可能与 wget 使用的用户代理 HTTP 标头有关)。

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>
RomainM
2020-07-17

此行为的原因在于 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'' ,因为我认为它实际上不是文件名的一部分。您也可以使用正则表达式来获取文件名,但这对我来说更容易。

Tomáš Šlancar
2020-07-17