这是 Python,而这是你熟悉的网站

这是一篇关于爬虫的文章,当然这篇文章并不是教大家去写爬虫程序,当然有兴趣的也可以学习一下有关爬虫和反爬虫的知识。

另外我还搭建了个网站,此次爬取文章的目的就是为了丰富网站的内容。防止我那弱不禁风、一碰即碎的网站被不怀好意之人给噶了,所以网址我就不透露给大家了。

1. 寻找目标

为了填充文章,思来想去,我觉得 掘金 和 CSDN 这两个网站是个不错的选择。另外掘金也没有做反爬,所以我们基本就是只要发起请求就能获取数据。

2. 分析网页

首先我们要获取的是文章的数据,所以我们随便点击一篇文章,并开启开发者工具,点击刷新,点击网络(Network)选项卡中,然后点击 Fetch/XHR,这样我们能指定获取的是通过 Fetch 或者是 XHR 来发起的请求。

image.png

但是这个文章我们不该点击这个选项,我们应该点击 全部,因为在文章这个页面,他用的是服务端渲染,即文章数据是随着 HTML 一起发送过来的,所以我们要爬取的链接就是能够获取到这个 HTML 的链接,即浏览器上方的输入框里面的链接,比如 https://juejin.cn/post/12304267413615312823,后面的一串数字就是文章对应的唯一 ID,我们只要能够获取到文章的 ID,就能够获取到文章的内容。

而首页的随机推送正好符合我们的需求,所以下面我们还要爬取首页的内容。

在首页打开开发者面板,点击刷新,在网络(Network)选项卡中,在过滤器输入框中输入 recommend_all_feed 关键词,我们可以找到这样一条信息。它的响应内容与主页上的推荐文章对应。这就是我们要找的请求,应该重点关注。

image.png

观察返回的数据结构。其中有文章的 ID、还有摘要,以及作者的信息。返回的文章的总条数为 20 条,多出来的两条是因为插入了 item_type: 14 的这两条数据,这个我们应该将其忽略。

image.png

3. 编写爬虫

首先是爬取首页的随机推送文章数据。我下面这段代码是 GPT 生成的,稍作修改得到的。

请求地址为 https://api.juejin.cn/recommend_api/v1/article/recommend_all_feed

def getArticleInfo():
    response = requests.post(url=url, params={
        "aid": 2608,
        "uuid": 7308645204243498534,
        "spider": 0,
    }, json={
        "client_type": 2608,
        "cursor": "0",
        "id_type": 2,
        "limit": 20,
        "sort_type": 200
    })

    # 检查请求是否成功
    if response.status_code == 200:
        data = json.loads(response.content)
        infos = data["data"]
        ids = []
        for i in range(0, len(infos)):
            if infos[i]["item_type"] != 2:
                continue
            info = infos[i]["item_info"]["article_info"]

            # 获取文章的标题、ID 和摘要
            ids.append({
                "title": info["title"],
                "article_id": info["article_id"],
                "brief_content": info["brief_content"],
            })
        return ids
    else:
        # 请求失败,打印错误信息
        print(f'请求失败,状态码:{response.status_code}')

在上面出现了三个字段,是我们通过分析后端接口返回的 JSON 数据得出的,其中 brief_content 是文章的摘要。

获取到了文章的 ID 之后,我们还要再请求获取文章数据的接口。由于文章的内容是在 HTML 标签中的,所以我们也只能获取到带有 HTML 标签的内容,因为我们通常会将其渲染在浏览器中进行浏览,同时为了保持文章的格式。

请求地址为:https://juejin.cn/post/文章ID

def getArticleContent(article_id):
    response = requests.get(url=f'{article_url}{article_id}', json={
        "cursor": "0",
        "id_type": 2,
        "item_id": str(article_id),
        "limit": 15,
        "referer": "",
        "user_id": "1442193974505742"
    })

    soup = BeautifulSoup(response.content, 'lxml')

    article_tag = soup.find(id="article-root")
    if article_tag is None:
        raise Exception("爬虫被发现了!")
    article_all_tags = article_tag.find_all(True)

    # 遍历文档中的所有标签
    for tag in article_all_tags:  # True 表示匹配所有标签
        # 遍历标签的所有属性
        for attr in list(tag.attrs):
            # 检查属性名是否以 'data-' 开头
            if attr.startswith('data-'):
                # 删除以 'data-' 开头的属性
                del tag[attr]

    return str(article_tag)

解释一下,其中在返回的 HTML 文档中有一个 id 为 article-root 的标签,这个标签是文章的内容的根标签,我们直接保存这个标签及下的子标签即可。

另外你还会看到删除 data- 开头的属性的代码,这是因为这些属性在我们的网页中并没有用处。因为我们的并不具备原网页的 css 或 script,并不能使其发挥作用。

获取到文章数据后,你可以将其保存到数据库中或者直接将返回的 HTML 文档写入到本地的 HTML 文件中进行保存。

下面是源代码,稍作修改即可运行

import requests
import json
from bs4 import BeautifulSoup
import mysql.connector
from time import sleep

# 接口的URL
url = 'https://api.juejin.cn/recommend_api/v1/article/recommend_all_feed' 
article_url = "https://juejin.cn/post/"

# 数据库连接配置
config = {
    'user': 'root',
    'password': 'password',
    'host': 'localhost',
    'database': 'database',
    'raise_on_warnings': True
}

# 连接到数据库
cnx = mysql.connector.connect(**config)

# 准备SQL插入语句
insert_query = """  
这是你的插入语句 
"""


# 获取文章信息
def getArticleInfo():
    response = requests.post(url=url, params={
        "aid": 2608,
        "uuid": 7308645204243498534,
        "spider": 0,
    }, json={
        "client_type": 2608,
        "cursor": "0",
        "id_type": 2,
        "limit": 20,
        "sort_type": 200
    })

    # 检查请求是否成功
    if response.status_code == 200:
        data = json.loads(response.content)
        infos = data["data"]
        ids = []
        for i in range(0, len(infos)):
            if infos[i]["item_type"] != 2:
                continue
            info = infos[i]["item_info"]["article_info"]
            ids.append({
                "title": info["title"],
                "article_id": info["article_id"],
                "brief_content": info["brief_content"],
            })
        return ids
    else:
        # 请求失败,打印错误信息
        print(f'请求失败,状态码:{response.status_code}')


# 获取文章内容
def getArticleContent(article_id):
    response = requests.get(url=f'{article_url}{article_id}', json={
        "cursor": "0",
        "id_type": 2,
        "item_id": str(article_id),
        "limit": 15,
        "referer": "",
        "user_id": "1442193974505742"
    })

    soup = BeautifulSoup(response.content, 'lxml')

    article_tag = soup.find(id="article-root")
    if article_tag is None:
        raise Exception("爬虫被发现了!")
    article_all_tags = article_tag.find_all(True)

    # 遍历文档中的所有标签
    for tag in article_all_tags:  # True 表示匹配所有标签
        # 遍历标签的所有属性
        for attr in list(tag.attrs):
            # 检查属性名是否以 'data-' 开头
            if attr.startswith('data-'):
                # 删除以 'data-' 开头的属性
                del tag[attr]

    return str(article_tag)


# 保存文章数据
def saveArticle(note_title, note_abstract, note_content):
    global note_id

    # 执行插入操作
    cursor = cnx.cursor()
    try:
        cursor.execute(insert_query, (value1, value2))
        cnx.commit()
        print("Data inserted successfully.")
    except mysql.connector.Error as err:
        print(f"Error: {err}")
    finally:
        cursor.close()



# 主函数
if __name__ == '__main__':
    while True:
        try:
            article_info = getArticleInfo()
            for j in range(0, len(article_info)):
                item = article_info[j]
                article_content = getArticleContent(item["article_id"])
                saveArticle(
                    item["title"],
                    item["brief_content"],
                    article_content
                )
                # 适当调整延迟
                sleep(5)
        except Exception as e:
            print(e)
        # 适当调整延迟
        sleep(20)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值