python自动化脚本及网络请求中抓取掘金文章榜列表

616 阅读3分钟

在之前的文章中,我们实现了python自动化浏览器的脚本,我们在里面用到了抓取页面的数据,但是我们是通过抓取浏览器dom元素,然后寻找指定标签来抓取标签的值,实现了最简单粗暴的数据抓取,但仅限于一些展现在页面上的数据,通常我们还有一些不需要展现给用户的,例如一些id、创建时间、地址url等。

这时候我们如果需要抓取一些不在页面上面展示的数据,上述的方法就不可行了,这时候我们需要从浏览器控制台的网络里面抓取后端返回给我们的数据,下面就让我们来看一下如何实现吧!

环境说明

我先列一下我的环境以及安装依赖的版本吧:

名称版本
Chrome浏览器124.0.6367.79
Chrome浏览器驱动124.0
python3.12
pip24.0
selenium4.19.0
selenium-wire5.1.0
ujson5.9.0

从环境的依赖中可以看出,比起上一次,我们新增了两个依赖,分别是selenium-wireujson

由于我使用的selenium版本较高,因此老的写法已经不支持了,所以只能安装selenium-wire,来抓取网络中的接口请求数据。

ujson则用来处理我们抓取的数据,将其进行格式转换。

需要注意的是,我们安装完selenium-wire之后,在调取的时候,会报一个blinker._saferef模块的错,网上找了一下没有找到合适的解决方案,可能是这个版本的问题吧,可以先将site-packages源代码中的部分先注释,暂时我没有看到在哪里使用了,我们将这部分先注释如下:

image.png

image.png

image.png

目标

我们以掘金首页为例,我们来抓取文章榜的数据列表,抓取到文章榜的文章名称作者

image.png

实现

  1. 关于打开掘金首页,并延时5秒关闭的代码,就不再讲解了,如果有不明白的,可以跳转至python自动化浏览器脚本说明,我们直接上代码部分,和上一期不同的就是引入webdriver的时候从seleniumwire中引入:

    import time
    from seleniumwire import webdriver
    from selenium.webdriver.chrome.service import Service
    
    driver = webdriver.Chrome(service=Service(r"C:\\driver\\chromedriver.exe"))
    driver.maximize_window()
    driver.execute_script("document.charset='utf-8';")
    driver.get("https://juejin.cn/")
    time.sleep(5)
    
  2. 抓取浏览器网络请求时候,我们可以通过driver.requests来抓取,但需要注意的是,这里因为版本比较高,我使用的是selenium-wire,这个前面已经说过了。我们找到文章榜的接口,例如: image.png

    我们在这个请求地址中截取一部分,用来判断接口,代码如下:

    for request in  driver.requests:
        if 'content/article_rank' in request.url:
            print(request.url)
    

    运行结果如下:

    image.png

  3. 获取网络请求的返回数据,可以使用下面的方式抓取

    from seleniumwire.utils import decode
    response_body = decode(request.response.body, request.response.headers.get('Content-Encoding', 'identity'))
    print(response_body)
    

    image.png 可以看到,我们已经抓取到了返回的数据,但是它是一个流数据,我们需要将其转化成utf-8的格式:

    decoded_response = response_body.decode('utf-8')
    

    这时候,decoded_response的值是一个utf-8格式的字符串,我们并不能直接访问其属性,因此需要转化成一个可以访问属性的json的数据,这里我使用的ujson进行的转换:

    dataJson = ujson.loads(json_str)
    print(dataJson) # 转化为json格式的输出
    
    dataList = dataJson["data"] # 返回数据中的列表字段
    
    for item in dataList:
        print(item["content"]["title"], item["author"]["name"]) # 输出文章榜的文章名和作者
    

    效果如下:

    image.png image.png

至此,我们就可以从网络请求中抓取想要抓取的数据了!

完整代码

import time
from seleniumwire import webdriver
from selenium.webdriver.chrome.service import Service
import ujson
from seleniumwire.utils import decode

driver = webdriver.Chrome(service=Service(r"C:\\tb2Lot\\driver\\chromedriver.exe"))
driver.maximize_window()
driver.execute_script("document.charset='utf-8';")
del driver.requests
driver.get("https://juejin.cn/")

for request in  driver.requests:
    if 'content/article_rank' in request.url:
        response_body = decode(request.response.body, request.response.headers.get('Content-Encoding', 'identity'))
        decoded_response = response_body.decode('utf-8')
        dataJson = ujson.loads(decoded_response)
        dataList = dataJson["data"]
        for item in dataList:
            print(item["content"]["title"], item["author"]["name"])
time.sleep(5)