Python(3) - 将来自 soup.find_all 的输出的特定 link 存储在变量中

Python(3) - store one specific link of output from soup.find_all in variable

我正在开发网页抓取功能。 我想从以下位置找到以“.img.html”结尾的最新下载 link:

"https://dl.twrp.me/gauguin/"

并将此 link 存储在一个变量中,而不仅仅是打印它。

到目前为止我的代码:

from bs4 import BeautifulSoup
from urllib.request import urlopen

url = "https://dl.twrp.me/gauguin/"

html_doc = urlopen(url)
# defining html link (twrp...)

soup = BeautifulSoup(html_doc, "html.parser")

for link in soup.find_all('a'):
    links = (link.get('href'))
print(links)

我的输出:

https://twrp.me
#
https://twrp.me/about/
https://twrp.me/contactus/
https://twrp.me/Devices/
https://twrp.me/FAQ/
/public.asc
/gauguin/twrp-3.5.2_10-0-gauguin.img.html
/gauguin/twrp-3.5.1_10-0-gauguin.img.html
/gauguin/twrp-3.5.0_10-0-gauguin.img.html
https://twrp.me/terms/termsofservice.html
https://twrp.me/terms/cookiepolicy.html
https://github.com/TeamWin

所以我的目标是过滤这个输出所以我只有那些link(最新的):

/gauguin/twrp-3.5.2_10-0-gauguin.img.html

存储在一个变量中,所以我可以稍后调用这个变量,甚至可以直接用 wget 下载它。

阅读您的查询后,我了解到您正在寻找一种方法来获取以“.img.html”结尾的链接,以便您将来可以使用它们。 下面的代码将提取所有目标链接并存储在一个 python 列表中,以后可以轻松使用。

你可以试试这个:

from bs4 import BeautifulSoup
from urllib.request import urlopen

url = "https://dl.twrp.me/gauguin/"

html_doc = urlopen(url)
# defining html link (twrp...)
soup = BeautifulSoup(html_doc, "html.parser")

links = []

for link in soup.find_all('a'):
    links.append((link.get('href')))

# target strings variable will contain all the links that end with .img.html 

target_strings =[]
for i in links:
    if '.img.html' in i:
        target_strings.append(i)

# and if needed in the future you can extract a single element from the list

当您遍历所有 <a> 元素时,您可以使用 str.endswith() 函数来检查 URL 是否以 .img.html 结尾。执行此操作以将所有 URL 提取到一个列表中:

urls = []
for link in soup.find_all('a'):
    url = link.get('href')
    if url.endswith('.img.html')
        urls.append(url)

其中给出了如下列表:

urls = ['/gauguin/twrp-3.5.2_10-0-gauguin.img.html',
'/gauguin/twrp-3.5.1_10-0-gauguin.img.html',
'/gauguin/twrp-3.5.0_10-0-gauguin.img.html']

接下来,这取决于您的 URL 中的 version-specifiers 是否保证跟在 lexicographic order 之后,即进行简单的字符串比较是否会得到最新的?如果它们都具有相同的格式,其中版本号的每个部分中的位数对于不同的字符串保持相同,这通常是正确的。您在示例中显示的那些满足此要求。

如果是这样,简单的做

max(urls)

这给出了

'/gauguin/twrp-3.5.2_10-0-gauguin.img.html'

如果不是这种情况,(例如,如果你有 '/gauguin/twrp-3.15.2_10-0-gauguin.img.html',这是数字 > 3.5.2 但不是字典顺序)你将不得不从你的文件中解析出版本号字符串,可能使用正则表达式,并比较这些版本号。您可以使用 max() 函数的 key 参数来执行此操作(敬请期待,我正在为此编辑我的答案)。

假设您的版本号采用 <numbers>.<numbers>.<numbers>_<numbers>-<numbers> 格式。您将使用以下正则表达式 (try it online):

\d+\.\d+\.\d+_\d+-\d+

Explanation
\d+    : One or more digits
\.     : The . character

要将它与 max() 一起使用,您可以编写一个从文件名中提取版本号的函数:

import re

def extract_version(filename):
    # e.g. filename = '/gauguin/twrp-3.5.2_10-0-gauguin.img.html'
    
    match = re.search(r"(\d+)\.(\d+)\.(\d+)_(\d+)-(\d+)", filename)
    # e.g. match = <re.Match object; span=(14, 24), match='3.5.2_10-0'>
    # e.g. match.groups() = ('3', '5', '2', '10', '0')

    if match is not None:
        return tuple(int(m) for m in match.groups()) # Convert match to tuple of integer for correct comparison

    return tuple() # if match is none, return an empty tuple

我对正则表达式所做的一个修改是将 \d+ 括在括号中。括号使它成为一个捕获组,因此所有数字部分都被捕获为单独的组。然后,re.search() returns 一个匹配对象,它的 .groups() 方法给出一个包含数字的元组,但作为字符串。所以我们需要在返回之前将这些字符串转换为整数。

然后,使用该函数作为 max()key 参数:

max(urls, key=extract_version)

我假设您想要 link 最近的日期。因此,您需要同时捕获 URL 和每个 link 的日期。然后可以将其转换为 datetime object 并添加到列表中。

找到所有 URL 后,列表可以很容易地按日期顺序排序,最新的在前。然后可以使用最新的URL来下载img文件。

例如:

from bs4 import BeautifulSoup
import requests
from datetime import datetime

base_url = "https://dl.twrp.me"
req = requests.get(f"{base_url}/gauguin")
soup = BeautifulSoup(req.content, "html.parser")

urls = []

for a in soup.find_all('a', href=True):
    link = a['href']
    
    if link.endswith('.img.html'):
        date_text = a.find_next('em').get_text(strip=True)
        date_dt = datetime.strptime(date_text, "%Y-%m-%d %H:%M:%S %Z")
        urls.append([date_dt, link])

latest = sorted(urls, reverse=True)[0][1]       # choose the latest url

# Download the latest img file
url_img = base_url + latest.split('.html')[0]
filename = url_img.split('/')[-1]

with requests.get(url_img, stream=True, headers={'Referer' : base_url + latest}) as req_img:
    with open(filename, 'wb') as f_img:
        for chunk in req_img.iter_content(chunk_size=2**15): 
            f_img.write(chunk)

如果更改命名或编号方案,此方法可能具有仍然有效的优势。添加了referer header以避免网站返回下载页面HTML。

这导致下载 131,072 KB


注意,如果您希望忽略日期而只按版本号排序,请使用以下方法:

from bs4 import BeautifulSoup
import requests
from datetime import datetime
import re

base_url = "https://dl.twrp.me"
req = requests.get(f"{base_url}/gauguin")
soup = BeautifulSoup(req.content, "html.parser")

urls = []

for a in soup.find_all('a', href=True):
    link = a['href']
    
    if link.endswith('.img.html'):
        version = re.findall(r'(\d+)', link)
        urls.append([version, link])

latest = sorted(urls, reverse=True)[0][1]       # choose the latest url