Python爬虫-01 初识爬虫

130 阅读11分钟

Python爬虫-01 初识爬虫

网络爬虫是一种自动化程序,能够高效抓取互联网上的公开数据(如商品价格、新闻资讯、社交媒体内容等),广泛应用于学术研究、市场分析、舆情监控和商业决策等领域。

  • 比如学术界使用爬虫追踪社会舆情、分析群体情感,
  • 电商平台通过爬虫监控竞品价格以优化定价策略,搜索引擎依赖爬虫建立网页索引等。

但爬虫存在一定技术门槛,为进一步打破数据壁垒,本教程系列拟从基础爬虫(含页面分析、基础代码编写、数据提取、数据储存、通用爬虫编写)和进阶爬虫(js逆向和app逆向)两个部分对爬虫技术进行阐述,这一章我们先来认识一下爬虫。

1 什么是爬虫

网络爬虫是使用python等工具编写的,按照编写者设定的规则,模拟浏览器发送网络请求,接收请求响应,自动抓取互联网信息的程序。从定义可以看出,如果技术足够精湛,能够通过浏览器做到的,爬虫都可以完成。

爬虫可以分为通用爬虫和自定义爬虫:

通用爬虫(如八爪鱼等)通过模拟浏览器访问网站,操作简单、兼容性强,适合大多数网站的数据采集,对初学者十分友好,但爬取速度较慢且对特殊数据的处理能力有限。

自定义爬虫则是针对特定网站开发的专用程序,无需浏览器即可直接获取数据,具有速度快、数据覆盖全的优势,但开发门槛高、周期长。用户可根据任务复杂度、开发周期和效率需求灵活选择适合的爬虫类型。当下AI工具在基础代码编写和逆向分析方面更为擅长,能够在一定程度上降低开发门槛和开发周期,即使初学者无代码基础,也能够写出令人满意的自定义爬虫。基于此,本教程两个板块都会涉及,以自定义爬虫为主,通用型爬虫为辅,协助各位完成爬虫程序开发。

爬虫类型数据可达性爬取时间开发时间
通用爬虫大多数数据
自定义爬虫基本所有数据

2 网络通信

当我们在网页上浏览各种信息时,是否想过这些数据从何而来?为什么输入网址就能获取内容?其实,这些数据都存储在远程服务器上。访问网页的过程,就是我们的设备向服务器请求数据的过程。

以抖音为例:当你想查看某条视频的评论时,你的设备会向抖音服务器发送请求,服务器收到后就会将评论数据传回,这样你就能看到具体内容了。但服务器不像邻居的电脑那样容易定位。我们如何找到这些远程服务器呢?关键就在于URL(统一资源定位符)。URL相当于数据的"门牌地址",通过它我们能精准定位数据存储的位置,从而发起访问请求。

image-20250323192906932.png

3 爬虫的基本流程

  1. 确定所需要的页面是静态页面还是动态页面
  2. 获取需要爬取的url(可能是网页链接,也可能是api接口)
  3. 通过程序向url发送请求,获取响应
  4. 解析并提取响应数据
  5. 数据储存

针对上面的流程,咱们依次来进行演示

3.1 判断页面类型

网页主要分为静态页面和动态页面两种:静态页面的数据直接嵌入在HTML文件中,内容固定不变,而动态页面的数据则由服务器实时生成或从数据库加载。要判断一个页面类型,可以在浏览器中右键点击"查看页面源代码",然后按Ctrl+F搜索页面上显示的内容——如果能找到匹配数据,说明是静态页面;若找不到,则说明是动态加载的页面。例如: 静态页面: image-20250323182406590.png Top250第一个数据是肖申克的救赎,在源代码中搜索,数据存在,所以豆瓣电影Top250这个网站是一个静态页面。 image-20250323182544357.png 接下来我们打开一个抖音短视频,点开评论区,点击查看网页源代码 动态页面:

接下来我们随便打开一个抖音短视频,点开评论区,点击查看网页源代码! image-20250323183147598-17434363409081.png image-20250323183303827.png 当我们搜索页面上显示的评论(如"没有我到处惹事,哆啦A梦能拍1000多集吗")却无法在HTML源码中找到时,即可判定这是一个动态页面。区分动态页面和静态页面的关键在于数据存储方式的不同——静态页面的数据直接嵌入HTML,而动态页面的数据通常由JavaScript实时加载。这种差异决定了我们需要采用不同的数据提取方法。

3.2 获取对应的url

我们之前提到要获取数据就需要得到相应的url。静态页面和动态页面提取url的方式相同,可以使用浏览器的开发者工具进行抓包。具体操作如下:按下F12键或右键点击选择"检查"即可打开开发者工具,在"Network"(网络)标签页中,可以查看所有网络请求信息,其中就包含我们需要的目标URLimage-20250323184800264.png 打开开发者工具后,我们点击键盘的F5键,刷新页面并重新获取数据。当数据加载后,点击页面的放大镜,在搜索框中输入相应的数据进行检索,以此便能定位到数据的来源(url和具体数据信息)。 image-20250323185051937.png image-20250323185455428-17434364012782.png

3.3 向服务器发送请求

现在我们已经获取了目标URL,接下来需要通过代码模拟浏览器向服务器发送请求。虽然编写请求代码听起来有些复杂,但我们可以借助一些现成的工具来简化这个过程。这里推荐一个实用的在线工具:spidertools.cn/#/curl2Requ… 具体操作步骤如下:

  1. 选中目标URL并右键点击
  2. 选择"复制"选项
  3. 在下拉菜单中选择"以curl(bash)格式复制"(注意:目前主流工具都支持bash格式解析,请确保选择此格式) 这个工具可以帮助我们快速生成Python的requests库代码,大大简化了开发流程。 image-20250324201654678.png 接下来将复制的curl指令直接复制到网站里面,网站就会生成python代码,我们可以复制将生成的代码,将代码放入pycharm、vscode等IDE中运行。 image-20250324202203975.png image-20250324202626645.png image-20250324202720500.png

3.4 数据解析

通过模拟浏览器发送请求的过程,我们能够直接通过代码获取到相应的数据。直接获取的数据过于冗余,为拿到所需要的数据,我们还需对原始数据进行提取。静态页面和动态页面因数据存储方式的不同,导致我们获取的原始数据有较大差异(如下图所示),因此会采取不同的方式提取这两种页面。 6f434e0e-4d40-4a5f-ac35-02c6fb98bdae.png 5bbf3d45-055f-496f-ab81-b1e0def8eb02.png image-20250324203757264.png

3.4.1 静态页面数据提取

静态页面的数据直接存储在HTML文件中。HTML本质上是一种标记文本文件,它通过特定的标签语法来定义内容结构和样式。例如,使用<h1>标题文字</h1>标签可以将文字设置为一级标题样式,类似的标签系统让HTML能够精确控制页面元素的呈现方式。这种标签的设置使相似元素有相同呈现方式,这给我们定位元素带来了便利。例如豆瓣电影top250的电影数据存在于span标签中,我们便能够通过定位span标签来定位数据。这里我们通过xpath来定位电影标题。(ps: xpath是一门在XML文档中查找信息的语言,在数据提取部分将着重提及,有兴趣的朋友可先自行百度了解)

a477848b-2e87-43fb-a4d1-6348013f8319.png 76e7db99-5c75-4bb9-8b1e-33ef10ae0b67.png

3.4.2 动态页面数据提取

要提取动态页面的数据,需要先分析其数据结构。以抖音评论为例,在开发者工具中可以看到类似这样的结构:

{
  "comments": [
    {
      "text": "《没有我到处惹事,哆啦A梦能拍1000多集吗》"
    }
  ]
}

这表示评论数据是以JSON格式存储的,最外层是包含comments键的字典,而每条评论又是一个包含text键的子字典。通过解析这个嵌套结构,就能准确提取出所需的评论内容。 5d65b363-6d3f-435a-b9fa-ddfa2f1b3ba5.png 再点击一下上面的预览,我们发现页面中有一个comments的字段。如果我们点开comments下面的数据,会发现每一条评论都会有具体的数据,包括ip位置,点赞数量,用户名之类的,具体字段提取需结合具体任务。 image-20250324204238492.png 由于网络传输返回的是JSON格式的字符串数据,直接处理字符串提取数据较为不便。我们可以通过调用.json()方法,将响应数据转换为Python字典格式,这样就可以方便地通过键值对来访问数据了。这里我提取了用户名、ip属地、评论、点赞数和回复数五个数据:具体实现如下: image-20250324205114813.png image-20250324211051080.png

3.5 数据储存

现阶段咱们的数据量不是很大,直接用csv来储存即可。csv文件是一个普通的文本文件,但是里面的每一行数据都是用,分开,csv文件可以直接用excel来打开,相对来说比较方便。

import csv # 导入csv的包,咱们操作都需要用这个包来完成

ans = []
title = ["用户名", "ip属地", "评论", "点赞数", "回复数"] # 字段

for item in response["comments"]:
    goods = item["digg_count"]          # 点赞数
    ip = item["ip_label"]               # ip地址
    text = item["text"]                 # 评论
    reply = item["reply_comment_total"] # 回复数量
    name = item["user"]["nickname"]     # 用户名称
    ans.append([name, ip, text, goods, reply]) # 用列表保存数据

with open("reply.csv", "a+", encoding="utf-8-sig", newline="") as f:
    f = csv.writer(f)
    f.writerow(title) # 写入字段信息
    f.writerows(ans)  # 写入数据

image-20250324212612663.png 至此,咱们爬虫的基本流程结束,这一节主要介绍了什么是爬虫,爬虫的基本流程。这一次的教程更多的是和大家一起走一次爬虫的基本流程,那么在之后的教程中将会进一步细化每一个过程。本章结束后各位可以去某联招聘网站逛一逛,尝试抓取招聘数据,熟悉流程。 咱们走完流程了,你可能在想,这么一通复杂的操作,咱们就拿下10条数据,还没直接复制快,所以,在最后写一段简单的爬评论代码,通过上面的解析网站提取ttwid和aweme_id,填写至下面代码即可运行,代码仅供学习参考。

import requests
import csv

class DouYin:
    def __init__(self, dyid):
        self.id = dyid
        self.headers = {
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
        }
        self.url = "https://www.douyin.com/aweme/v1/web/comment/list/"
        with open("reply.csv", "w+", encoding="utf-8-sig", newline="") as f:
            f = csv.writer(f)
            f.writerow(["用户名", "ip属地", "评论", "点赞数", "回复数"])

    def request_comments(self, start):
        '''发送一次请求,获取10条评论数据,a_bogus不做验证,可固定'''
        params = {
            "aid": "6383",
            "aweme_id": self.id,
            "cursor": str(start),
            "count": "10",
            "a_bogus": "Oj45gw7wdNWcFd/SmKBfSWCUhznMNT8yKqT2bqdUexxiGHUbcSPUZnSTroKU/d/7ubBzhH1HrfGAbjdcQ4thZK9kwmhkSQsfOz/I9g8o8qqhTlG/DrRuChUzKw0e0mkNeA9ji1mRUs0rIjnRnq5uAppae5zoQ5EgSHBVp2SyJDSW3syTI9dbC-iAnwE=",
        }
        try:
            response = requests.get(
                self.url, headers=self.headers, cookies=cookies, params=params
            ).json()
        except Exception as e:
            print("[ERROR]: ", e)
            response = {}
        return response.get("comments")

    def request_comments_pages(self, counts):
        '''main函数,用于获取指定数量评论'''
        for count in range(0, counts, 10):
            result = self.request_comments(count)
            if result is None:
                break
            ans = []
            for item in result:
                goods = item.get("digg_count")  # 点赞数
                ip = item.get("ip_label")  # ip地址
                text = item.get("text")  # 评论
                reply = item.get("reply_comment_total")  # 回复数量
                name = item.get("user", {}).get("nickname")  # 用户名称
                ans.append([name, ip, text, goods, reply])
            self.save_data(ans)
            print(f"[SUCCESS]: {count + len(ans)}条数据保存成功....")

    def save_data(self, data):
        '''用于保存数据'''
        with open("reply.csv", "a+", encoding="utf-8-sig", newline="") as f:
            f = csv.writer(f)
            f.writerows(data)

if __name__ == "__main__":
    ttwid =                 # cookie信息,需要ttwid字段,只需要修改一次
    aweme_id =              # 视频的id,需要aweme_id字段,不同视频aweme_id不同
    counts = 1000           # 需要的评论数量

    cookies = {"ttwid": ttwid}
    dy = DouYin(aweme_id)
    dy.request_comments_pages(counts)

image-20250324221638606.png 关于aweme_id和cookie的获取,不同视频的aweme_id是不同的,换一个视频需要更换aweme_id,但是cookie是不需要的修改的,填了一次之后就不用再填了。 image-20250325095938607.png image-20250325100143909.png