如何在 Medium 上从您最喜欢的出版物中抓取文章
欢迎大家,希望你有一个美好的一天!今天我们将构建一个简单的 python 网页抓取脚本,根据您想要的任何日期、月份或年份来抓取您最喜欢的medium.com出版物的热门文章!如果你坚持到最后,我将向你展示如何使用 pythonsThreading模块,这会导致极快的网络抓取。
以下是我们今天要使用的工具:
-
Python 编程语言
-
美汤套餐
-
请求包
-
线程模块
-
与文件系统集成
目录:
-
什么是网页抓取?
-
编写脚本
*你需要什么模块?
*如何从 Medium 上的出版物中获取文章?
*如何进行网络请求?
*如何用 Beautiful Soup 解析 HTML?
*如何将数据写入文件?
*BONUS — 如何使用线程提高性能
- 源代码
什么是网页抓取?
在深入研究代码之前,我可能应该解释一下网络抓取到底是什么。网络抓取是从互联网上提取信息,很简单吧?好的,所以我们知道什么是网络抓取,但我们为什么需要它,它有什么用处?
网络抓取是如此广泛,以至于我们无法将其缩小到单一用途,但网络抓取的一些常见需求包括数据收集和自动化重复任务(嗯,这听起来非常有用)。当然,您几乎可以手动执行任何可以进行网络抓取的操作,但如果您像我一样,您更愿意避免无聊、重复的任务。加上编写一些代码来为你工作,听起来就像是在推进你的生活。
你需要什么模块?
在开始编码之前,我们必须确保安装了所有正确的包。对于这个脚本,我们需要使用 pip 安装requests和beautifulsoup4模块。
pip install requests
pip install beautifulsoup4
requests模块是我们实际向Medium.com发出网络请求并获取其数据的方式,而beautifulsoup4模块将为我们解析这些数据并返回我们真正关心的数据。 (哦,从现在开始我将把 BeautifulSoup 模块称为 bs4)
如果你想深入了解这些模块,这里有两篇来自_Real Python_的精彩文章,requests,bs4
现在我们已经安装了模块,我们将创建一个get_medium_articles.py脚本并导入所有内容。

如何从 Medium 上的出版物中获取文章?
在开始获取数据之前,我们需要了解的是真正了解我们需要到达的 URL。在 Medium 中,每个出版物都有自己唯一的 URL,这很好,但 Medium 对我们来说更进一步,因为我们希望根据发布日期找到不同的文章。
我们如何实现这一目标?好吧,我们所要做的就是添加
/archive/YEAR-MONTH-DAY
到 URL 的末尾和指定日期的热门文章。这让我们的生活更轻松,因为我们所要做的就是生成我们想要的日期(这是datetime模块的来源),将其添加到发布 URL,然后我们就可以开始获取数据了。
因此,让我们首先查找所有您喜欢的出版物并创建一个字典,该字典将出版物名称存储为键,并将其 URL 作为值,并在末尾附加 /archive/。
您可能会注意到我们没有在此处添加任何日期,那是因为我们将通过程序动态生成它们(我将坚持使用当前日期,但您可以完全控制它)。一个提示是,您可以排除日期中的日期或月份,以获取某个月份或年份的热门文章。

创建 URL 字典后,我们将创建一个名为fetch_articles的函数,它将接收出版物和 URL 并返回我们想要的数据!
让我们继续初始化一个名为data的空字典,然后让我们继续获取当前日期,我们将需要datetime.now()函数返回当前日期和时间,然后在日期上使用strftime方法以某种方式正确格式化它会明白。
只是一个快速提示,如果您在一大早这样做,那么很有可能还没有任何文章,所以从前一天获取可能是个好主意,如果是这种情况,您可以将get_articles函数中的日期更改为
yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
date = str(yesterday.strftime("%Y/%m/%d"))
接下来,我们需要将日期附加到 URL 的末尾并打印出出版物给我们一些反馈,或者如果您愿意,您可以在此处打印出 URL。 (如果您好奇,可以继续查看 URL 并更改日期,看看会发生什么!)
如何进行网络请求?
对于这一步,我们需要做的就是执行一个 GET 请求。什么是 GET 请求?这里我们需要知道的是,它是否会转到一个 URL 并从该 URL 获取所有数据,这可以是 JSON、HTML 等。在这种情况下,我们将得到的是我们将输入的 HTML bs4 来获取我们的数据。所以事不宜迟,让我们开始构建我们的函数😄。

我们通过调用requests.get()并将 URL 传递给它来发出 GET 请求。这将返回一个响应对象,我们要做的第一件事是调用raise_for_status(),因为我们希望在编码时尽可能早地崩溃,因为它总是会在更接近问题的地方崩溃。
在这种情况下,如果您提供了无效的 URL,或者存在连接问题,程序将会崩溃。
我决定捕捉这个异常并打印出无效的 URL。只要请求有效,我们就可以通过在响应对象上调用.content来访问请求中的 HTML,这就是我们接下来要提供 bs4 的内容!
好的,你们中的一些人一定会问,“allow_redirects=False是做什么的?”。如果你发现了,那就太好了,但让我解释一下。
在 Medium 上,如果您在没有文章发表的那一天通过,那么它将重定向到文章发表的上个月,并且因为我只关心从今天开始查找所有新的相关文章,所以我选择禁止重定向。如果这是您想要的功能,那么只需设置allow_redirects=True。
如何用 Beautiful Soup 解析 HTML?
在我们实际对代码进行任何操作之前,让我们访问您选择的任何出版物并输入您要查看文章的日期,我将转到betterprogramming.pub/archive/2021/08/18,然后打开起来我的检查员。

如果您使用选择器工具(command/control+shift+c)并将鼠标悬停在一篇文章上,您将看到一个类名为class="postArticle postArticle--short js-postArticle js-trackPostPresentation js-trackPostScrolls"的 div,这就是 bs4 将用来查找页面上每篇文章的内容。

现在我们知道要查找什么,让我们创建一个BeautifulSoup对象并传入页面的内容,对于第二个参数,我们将传入html.parser,它告诉 bs4 解析 HTML。回过头来看,我们可以看到这些类都在一个 div 中,因此我们需要告诉我们的新 bs4 对象来查找页面上具有这些特定类的所有 div。

得到所有文章后,如果我们发现任何文章,我们会给自己一些终端反馈。接下来,我们将确定我们想要多少篇文章,我选择每个出版物 3 篇(或更少),但您可以拥有任意多的文章。

哇,代码太多了,下一部分可以更进一步,但我选择只从文章中检索标题和 URL(我挑战你也得到作者!)。让我们分解它并将 HTML 转换为我们可以使用的东西!
目前,我们有一个文章列表,所以让我们一个一个循环并提取数据。首先,我们将使用find()方法提取标题,对其进行验证,然后检索文本字符串。
下一行看起来很混乱,但让我们看看,发现我们正在找到所有a标签(链接),找到合适的文本,得到它是href,从 URL 中删除参数然后返回它。
我计划存储数据的方式是有一个以出版物为键的字典,其值为文章数组,所以让我们继续创建文章,然后将其与当前出版物放在一起。
我只想说恭喜你已经做到了这一步,如果你只是来这里获取数据,那么你就完成了,因为在你遍历所有出版物之后,你的字典将充满你需要的所有数据!
但是,如果您想了解如何以漂亮的 Markdown 格式将其输出到您的桌面,请继续阅读 :)(我还在最后展示了线程)
如何将数据写入文件?
对于下一部分,我们不需要任何特殊的模块或包,我们需要的所有东西都内置在 python 中(多么好)。
首先,我们将创建一个名为write_to_desktop(data)的新函数,我计划将我们所有的数据写入我桌面上的 Markdown 文件,但你可以用它做任何你想做的事情,例如使用简单的文本文件或写入单独的文件驾驶。

我们首先在您的桌面上创建(或打开)一个文件并初始化一个空字符串,这就是最终写入文件的内容。
接下来,我们将编写两个 for 循环,第一个用于获取出版物和它的文章,第二个用于获取每篇文章的数据。如果主题标签和星号对您来说看起来很吓人,那只是为了让降价看起来不错,如果您正在写入文本文件,您可以忽略它们。
然后我们将我们的数据写入文件,我们就差不多完成了,让我们写出我们的main()函数并实际看到一些结果!


继续运行你的脚本和 BOOM,你就完成了。恭喜......嗯,我想我确实向你们保证了奖金。让我们去提高性能🚀
奖励 - 如何使用线程提高性能
在进入本节之前,我确实想指出这可以大大提高性能,但是,如果您只从少数出版物中获取文章,您不会看到太大的变化,但是一旦您从超过 5 个出版物中获取,性能提升真正开始发挥作用。
我们这里唯一需要修改的函数是我们的main()函数,所以这不会太复杂。首先,我们需要将threading模块导入到我们的脚本中。

然后,我们将为每个发布创建一个新线程,所以继续调用threading.Thread将目标生成get_articles脚本,args中的所有内容都是将进入get_articles的参数。
之后,我们会将线程附加到我们的线程列表中,然后启动线程。
下一个 for 循环可能看起来有点奇怪,但我们这样做是为了让所有线程在写入数据之前完成。如果没有这个循环,我们的数据将只包含来自单个线程的数据,因为write()函数会在我们的数据准备好之前被调用。
现在尝试在顶部的字典中添加更多出版物,看看您是否注意到任何性能差异!
再次恭喜,我们正式完成了这个脚本。
结论
哇,这很多,我们讨论了什么是网络抓取,发出网络请求,抓取数据,写入文件,以及使用线程。希望您可以在工作流程的某个地方找到此脚本的用途!就个人而言,我每天早上都使用它,因为我使用黑曜石日常笔记,并且我有这个脚本将所有文章附加到笔记的底部,以便我在喝一杯好咖啡时查看☕️。请在下面评论您打算如何使用此脚本!
无论如何,我真的希望你喜欢这篇文章,如果你愿意,可以在Twitter上与我交谈!祝你有美好的一天! (代码就在下面)
zoz100077 * *
源码
import requests, datetime, threading
from bs4 import BeautifulSoup
urls = {
"Towards Data Science": "https://towardsdatascience.com/archive/",
"Personal Growth": "https://medium.com/personal-growth/archive/",
"Better Programming": "https://betterprogramming.pub/archive/",
}
data = {}
def get_articles(publication, url):
yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
date = str(yesterday.strftime("%Y/%m/%d"))
url += date
print(f"Checking {publication}...")
response = requests.get(url, allow_redirects=False)
try:
response.raise_for_status()
except Exception:
print(f"Invalid URL At {url}")
page = response.content
soup = BeautifulSoup(page, "html.parser")
articles = soup.find_all(
"div",
class_="postArticle postArticle--short js-postArticle js-trackPostPresentation js-trackPostScrolls",
)
if len(articles) > 0:
print(f"Fetching Articles from {url}")
amount_of_articles = min(3, len(articles))
for i in range(amount_of_articles):
title = articles[i].find("h3", class_="graf--title")
if title is None:
continue
title = title.contents[0]
article_url = articles[i].find_all("a")[3]["href"].split("?")[0]
article = {
"title": title,
"article_url": article_url,
}
if not data.get(publication):
data[publication] = [article]
else:
data[publication].append(article)
def write_to_desktop(data):
with open("PATH_TO_DESKTOP/articles.md", "a") as file:
out = ""
for publication, articles in data.items():
out += f"### ***{publication}***\n"
for article in articles:
out += f"#### [{article['title']}]({article['article_url']})\n\n"
out += "---\n\n"
file.write(out)
def main():
threads = []
for publication, url in urls.items():
thread = threading.Thread(target=get_articles, args=[publication, url])
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
write_to_desktop(data)
main()
更多推荐

所有评论(0)