从 0 到 1 学爬虫:用 Python 爬取网页数据指南一(附实战案例)

引言

        在我们的生活中会遇到很多的数据,或者是自己想看的照片,或者是电影评分,一些商品比较。这个时候,如果我们手动收集需要消耗很多的时间,精力。此时爬虫就在数据收集、分析中体现了其使用价值。而本文则简要的叙述关于爬虫的相关知识。

一、爬虫入门:核心库与基础原理

1.1 基础工具的了解

        对于爬虫,如同网络安全和网络工程一样,抓包是一项必不可少的工作,但是我们没有类似网络安全那样使用BP或者是网络工程那般使用wireshark,通常情况下利用浏览器自带的抓包即可(这里推荐使用google或者是微软)接下来便会介绍一下工具的使用,首先打开F12

        如图中圈出的所示,我们主要使用的是Elements、Console、Sources以及Network。

        对于Elements大家会误认为是源代码,实则不然,如果想查看源代码,则应该使用鼠标右键的检查代码。如图所示:

        那就有人会问:这两个有什么区别呢?拿考试举例子来说,查看源代码,相当于老师给出的卷子的标准答案;而Elements是由学霸自己写出的不是很简洁的答案。可是,那是不是意味着Elements是没有用的呢?答案是错误的,在一个只有代码的页面里,我们想要找到我们所需要的标签是困难的,有时候便会须有借助Elements去定位我们所需要的标签。

        console是一个用来调试JavaScript的工作台,在爬虫中用处不是很大,下文如有需要会单独去说。

        Sources是存放了源代码的文件夹,可以查看其文件。

        Network则是我们用的最多的,要注意打开这些选项,当刷新后会出现所有传输的数据包,有时候我们想要的便在这里。

1.2 基础库urllib

        引入库 urllib.request。即

from urllib.request import urlopen

        接下来以百度为例,在urllib库中引用urlopen方法:

url = "http://www.baidu.com"  # 定义要访问的url

resp = urlopen(url)  # 打开url
#print(resp.read().decode("utf-8"))  # 读取响应内容并解码为utf-8,此时拿到的是源代码

with open("mybaidu.html",mode="w",encoding="utf-8") as f:   # 打开文件,指定编码为utf-8
    f.write(resp.read().decode("utf-8"))  # 写入文件

        首先我们需要给出一个url,告诉爬虫我们要去哪里爬,所以这个时候就要利用urlopen()方法进行获取url。

        而resp.read()获取的其实是字节,所以需要通过解码的形式,获取到我们能看懂的源代码。但是解码的码源对于不同网站是不一样的,所以此时需要用CTRL+F搜索“charset”这个单词来查找发现使用的码源,百度用的是“UTF-8”.如图:

       

        图一是未使用解码的效果,图二是使用了解码的效果。

        到这里我们并没有将其保存,所以为了将其保存,我们使用了函数即“with open() as”

        python文件读写,以后就用with open 语句。

        

with open("filename.txt",'r') as f:
	content = f.read(f)   #文件的读操作

        其中意思为保存文件类型为txt,以只读的方式打开文件。

with open("data.txt","w") as f:
    content = f.write("hello world")   #文件的写操作

        其中意思为保存文件类型为txt,以只用于写入的方式打开文件

        故,对于我们想要保存的源代码,则需要用到如此语句

with open("mybaidu.html",mode="w",encoding="utf-8") as f:
    f.write(resp.read().decode("utf-8"))

        该代码段的意思是,将url的页面用utf-8的形式解码写入到mybaidu.html中并保存。

        但是需要注意的是:urllib.requests库是自带的,并不好用,所以引入新的库requests。该库需要下载,在终端输入pip install requests进行下载。下载完成后便可正常使用。

import requests

url = "http://www.baidu.com"

resp = requests.get(url)  #发送get请求体
resp.encoding = "utf-8"
print(resp.txt)  #拿到页面源代码

1.3 POST与GET请求

1.3.1 POST请求

        以百度翻译为例

import requests

url = "https://fanyi.baidu.com/sug"   #对于post无论url有多长都需要拿过来

data ={
    "kw":input("请输入一个单词:")
}

rep = requests.post(url,data=data)   #data为post请求的数据包参数

print(rep.text)   #拿到的是文本字符串
print(rep.json()['data'])  #此时拿到的直接是json数据

        对于data部分,有人可能会问,这是在哪里呢?我们在百度翻译里输入dog(最后使用中文输入)

        抓包后,我们可以在form data中找到相应的数据,如下图所示:

1.3.2 GET请求

        以豆瓣为例:

import requests

#get请求只需要提取问号前面的url
url = "https://movie.douban.com/j/chart/top_list"

data = {
    "type":"13",
	"interval_id":"100:90",
	"action":"",
	"start" :"0",
	"limit":"20"
}

header = {
    "user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36"
}

resp = requests.get(url,params=data,headers=headers)   #处理get请求数据包为params

print(resp.text)
print(resp.request.url)   #查看url

        对于data部分,在Get请求中为Query String Parameters,与POST请求在同一个位置,需要注意的是GET请求只需要提取问号前面的URL,而非整个的URL。

二、网页解析:从 HTML 中提取数据

2.1 HTML与CSS

2.1.1 HTML基础语法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>标题</title>
</head>
<body>

</body>
</html>

<!--
html基本语法:
1.<标签名 属性="属性值">被标记的内容</标签名>
2.<标签名 属性="属性值" />

2.1.2 CSS基础语法

        完整的 CSS 代码由 “选择器” 和 “声明块” 组成,但爬虫场景下,我们只需要 “选择器”

需求场景推荐选择器示例
定位唯一元素ID 选择器(#id)#user-avatar → 定位用户头像
定位一类元素(如商品列表)类选择器(.class).goods-item → 定位所有商品项
定位嵌套元素(如列表里的链接)后代选择器(空格).goods-list a → 定位商品列表里的所有链接
定位带特定属性的元素(如图片)属性选择器([属性])img[src] → 定位所有图片(带 src 属性的 img 标签)

2.2 正则表达式

2.2.1 元字符

.       匹配换行以外的任意字符,在python的re模块中的是一个坑
\w      匹配字母或数字或下划线
\d      匹配的是数字
\n      匹配一个换行符
\t      匹配一个制表符
​
^       匹配字符串的开头
$       匹配字符串的结尾
​
\W      匹配非字母或数字或下划线
\D      匹配非数字
\S      匹配非空白符
a|b     匹配字符串a或字符串b
​
()      匹配括号内的表达式,也表示一个组
[]      匹配字符组中的字符

2.2.2 量词

*       重复零次或更多次
+       重复一次或更多次
?       重复零次或一次
{n}     重复n次
{n,}    重复n此或更多次
{n,m}   重复n到m次

2.2.3 惰性匹配和贪婪匹配

.*      贪婪匹配(尽可能多的拿到结果,匹配到最后一个出现在*后的)
.*?     惰性匹配(尽可能少的拿到结果,匹配到第一个出现在*后的)

2.2.4 以爬取豆瓣top250为例

思路:我们需要先拿到页面源代码(仔细观察其url每个页面就是对于其实值加5所产生的等差数列),再用正则去爬取数据,之后将爬取的数据保存下来。

#思路:先拿到页面源代码,再用正则提取数据,保存数据

import requests
import re

#csv文件数据与数据之间用问号隔开
f = open("top250.csv",mode="w",encoding="utf-8")

for i in range(0,250,25):
    data = i
    url = f"https://movie.douban.com/top250?start={data}"

    header  = {
        "user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36"
    }

    resp = requests.get(url,headers = header)
    content = resp.text


    #编写正则
    # re.S可以让正则中的.可以匹配换行符
    obj = re.compile(r'<div class="item">.*?<span class="title">(?P<name>.*?)</span>.*?<p>.*?导演: (?P<daoyan>.*?)&nbsp.*?主演:(?P<zhuyan>.*?)<br>(?P<year>.*?)&nbsp.*?<span class="rating_num" property="v:average">(?P<pingfen>.*?)</span>.*?<span>(?P<num>.*?)人评价</span>',re.S)

    result = obj.finditer(content)
    for item in result:
        name = item.group("name")
        daoyan = item.group("daoyan")
        zhuyan = item.group("zhuyan")
        year = item.group("year").strip()  #去掉字符串两边的空白
        pingfen = item.group("pingfen")
        num = item.group("num")
        f.write(f"电影名称:{name}\n导演:{daoyan}\n主演:{zhuyan}\n年份:{year}\n评分:{pingfen}\n{num}人评价\n")

#收尾工作
f.close()
resp.close()
print("数据保存成功")

2.3 BS

2.3.1 BS知识点总结

        BeautifulSoup 是一个 Python 库,用于从 HTML 或 XML 文件中提取数据。它的主要优势在于能将复杂的 HTML 文档转换成一个结构化的树形对象,让你能轻松地通过标签名、属性、文本等方式来查找和操作节点。

        其安装方式依旧是(pip install beautifulsoup4);导入方式为(from bs4 import BeautifulSoup)

        使用BeautifulSoup的第一步。需要将HTML/XML文本或文件对象传入到BeautifulSoup构造函数,并指定一个解析器。如代码所示,soup对象现在代表了整个html文档的结构,你可以把它想象成一个树的根节点

from bs4 import BeautifulSoup

# 假设 html 是你的 HTML 字符串
html_doc = "<html><head><title>标题</title></head><body><p>内容</p></body></html>"

# 创建 BeautifulSoup 对象
# 第一个参数是要解析的文本
# 第二个参数是解析器,推荐使用 "html.parser" (Python 内置,无需额外安装)
# 其他选项有 "lxml" (速度快,功能强,但需要 pip install lxml) 和 "html5lib" (兼容性好)
soup = BeautifulSoup(html_doc, "html.parser")

        BeautifulSoup 最常用的功能是

soup.find()查找第一个匹配条件的元素。一个 Tag 对象,如果找不到则返回 Noneli = page.find("li", attrs={"id": "abc"})
soup.find_all()查找所有匹配条件的元素。一个包含 Tag 对象的列表 (list),如果找不到则返回空列表 []li_list = page.find_all("li")

        其查找条件:

  • 通过标签名soup.find("li")
  • 通过属性soup.find(attrs={"id": "abc"})
  • 标签名 + 属性 (最常用): soup.find("li", attrs={"id": "abc"})
  • 简化写法: 对于 id 和 class 属性,可以直接作为关键字参数传入。
    • soup.find("li", id="abc") # 注意 class 是 Python 关键字,所以用 class_
    • soup.find("div", class_="container")

        遍历 find_all() 的结果find_all() 返回一个列表,你可以用 for 循环来遍历每一个元素。

li_list = page.find_all("li")
for li in li_list:
    # 对每个 li 元素进行操作
    print(li)

        嵌套查找: 你可以在一个已经找到的元素(Tag 对象)中继续使用 find() 或 find_all() 进行查找。

# 先找到 id 为 abc 的 li 标签
li = page.find("li", id="abc")
# 再在这个 li 标签内部查找 a 标签
a = li.find("a") 

        找到元素后,通常需要提取它的文本内容或属性值。此时就需要.text或.get_text()。对于后者可以提供多个参数,如:strip=True 可以去除首尾空白,separator=' ' 可以指定拼接符。

a_tag = page.find("a")
print(a_tag.text)  # 输出: 张三

2.3.2 实战演练

import requests
from bs4 import BeautifulSoup
import re

"""
子页面的url如果开头是/,直接在前面拼接上域名即可
子页面的url不是/开头,此时需要找到主页面的url,去掉最后一个/后面的所有内容,再拼接上子页面的url
"""

url = 'https://umei.net/tags/nvpu/'
domain = "https://umei.net/"

resp = requests.get(url)

obj = re.compile(r'<li class="i_list list_n2">.*?<a href="(?P<href>.*?)" title=',re.S)
main_page = BeautifulSoup(resp.text,"html.parser")
result1 = obj.finditer(resp.text)

n = 1

for item in result1:
    href = item.group("href")
    child_url = domain + href
    child_resp = requests.get(child_url)
    child_resp.encoding = "utf-8"
    #子页面的bs对象
    child_bs = BeautifulSoup(child_resp.text,"html.parser")
    #子页面的图片url
    div = child_bs.find("div", attrs={"class":"image_div"})
    img_src = div.find("img").get("src")    #拿到图片的下载路径
    #下载图片
    img_resp = requests.get(img_src)   #图片不是文本,不能获取text的内容,所以应该用字节的形式
    img = img_resp.content
    with open(f"{n}.jpg",mode = "wb") as f:
        f.write(img_resp.content)
    print(f"第{n}张图片下载完成")

    n += 1

f.close()
child_resp.close()
resp.close()

        该代码对于一个图片的保存做了很好的阐述,因为其不是文本,所以我们不能用之前那种编码,使用的更应该是字节的形式。

2.4 XPath

2.4.1 知识点分析

<book>
    <id>1</id>
    <name>测试</name>
    <price>1.23</price>
    <author>
        <nick>name1</nick>
        <nick>name2</nick>
    </author>
</book>

        对于上述代码做出总结:

  1. book,id,name,price...都被称为节点

  2. Id,name,price,author被称为book的子父节点

  3. book被称为id,name,price,author的父节点

  4. id,name,price,author被称为同胞节点

2.4.2 实战演练

"""
拿到页面源代码
从页面源代码获取所有的项目名称
"""

import requests
from lxml import etree

url = "https://www.zbj.com/fw/"

f =  open("猪八戒.csv",mode="w",encoding="utf-8")

data = {
    "type":"news",
    "kw":"saas"
}

haeders = {
    "user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36"
}

resp = requests.get(url,params=data,headers=haeders)


er = etree.HTML(resp.text)
#search-result-list-service,准备变量来收
divs = er.xpath("//div[@class='search-result-list-service']/div")
for div in divs:
    price = div.xpath("./div/div[3]/div[1]/span[1]/text()")[0]
    tltle = div.xpath("./div/div[3]/div[2]/div/span//text()") #表示提取span中的所有
    tltle = "".join(tltle)
    company = div.xpath("./div/div[5]/div/div/div/text()")[0]
    f.write(f"项目名称:{tltle},价格:{price},公司名称:{company}\n")




2.5 PyQuery

PyQuery 是一个模仿 jQuery 语法的 Python 库,它能让你像在浏览器中操作 DOM 一样方便地解析 HTML。

  • 核心思想:将 HTML 解析成一个树状结构(DOM 树),然后通过强大的CSS 选择器来定位和操作其中的节点。
  • 在代码中的应用
    • 初始化pq(html_string) 将 HTML 字符串加载成一个 PyQuery 对象。
    • CSS 选择器:这是其核心优势。
      • p("a"):选择所有 <a> 标签。
      • p(".item a"):选择 class 为 item 的元素下的所有 <a> 标签。
      • p("#qq a"):选择 id 为 qq 的元素下的所有 <a> 标签。
    • 提取数据
      • .attr("href"):获取选中节点的 href 属性值。
      • .text():获取选中节点的文本内容。
    • 遍历多个元素:当选择器匹配到多个节点时,.items() 方法会返回一个迭代器,方便你逐个处理每个节点。
    • DOM 操作:PyQuery 不仅能读,还能修改。如 .after().append().attr().remove() 等方法,可以动态地添加、修改或删除标签和属性。

三、对与电影天堂的爬取

"""
提取到主页面中的每一个电影背后的那个URL地址
    拿到2021必看热片的html代码
    从刚才拿到的html代码中提取到href的值

访问子页面,提取到电影的名称以及下载地址
"""

import requests
import re

f = open("电影天堂.csv",mode="w",encoding="utf-8")

url = "https://www.dytt8899.com/"
resp = requests.get(url)
resp.encoding = "gbk"

#1.提取到必看热片代码
obj = re.compile(r"2025必看热片.*?<ul>(?P<html>.*?)</ul>",re.S)
result1 = obj.search(resp.text)
html = result1.group("html")

#2.提取a标签中的href属性
obj2 = re.compile(r"<li><a href='(?P<href>.*?)' title")
result2 = obj2.finditer(html)

#爬虫要对id敏感,所以采取id进行定位
obj3 = re.compile(r'<br />◎片  名(?P<name>.*?)<br />.*?<td style="WORD-WRAP: break-word" bgcolor="#fdfddf"><a href="(?P<url>.*?)">',re.S)

for item in result2:
    url2 = url.strip("/") + item.group("href")
    child_resp = requests.get(url2)
    child_resp.encoding = "gbk"

    result3 = obj3.search(child_resp.text)
    movie = result3.group("name")
    download_url = result3.group("url")
    f.write(f"电影名称{movie}/t下载地址{download_url}\n")

f.close()
resp.close()
child_resp.close()
print("数据已保存")

四、一个简单的模拟用户登录的程序

        模拟用户登录实际是获取其Cookie值来进行登录,cookie值的抓取需要通过数据包来获得。

#登录 -> 得到cookie
#带着cookie 去请求到书架url -》 书架上的内容

#把上述操作连续起来
#我们可以使用session请求 -> session你可以认为是一连串的请求,在这个过程中的cookie不会丢失
import requests

#会话
session = requests.session()    #拿session对象

#1.登录
url = "https://passport.17k.com/ck/user/login"
data = {
    "loginName":"Freedom049527",
    "password":"sin(30)Msy049527"
}

#session.post(url,data=data,headers=headers)
#print(resp.text)
#print(resp.cookies)
#2.那书架上的内容
resp = requests.get("https://user.17k.com/ck/author2/shelf?page=1&appKey=2406394919",headers={
    "Cookie":"GUID=25c4d053-d289-43e0-87b9-a20d76772fc1; sajssdk_2015_cross_new_user=1; Hm_lvt_9793f42b498361373512340937deb2a0=1758178121; HMACCOUNT=B385CD001FAA3161; c_channel=0; c_csc=web; acw_sc__v2=68cbaed0dea5efb1275723ff87124fbf4bd8c39b; ssxmod_itna=eu0Q0KAI3x8f40L9YtDymDBQnD7DmEidM0fu4GXhmYDZDiqAPGhDC3bUgd4QQ2qEWDp04r3=58QnwvW=tBWfPirDCPGnDBFhfqQxYYkDt4DTD34DYDi2KDLDmeD+GqKDdEsNXzS2D3qDwDB=DmqG2ld=Dm4DfDDLn30qGbLQD4qDBDGtx2jNDG4Gf7uqD0LEIDohuDxGWbTtjYnNqcjDgYY=DjqGgDBLelcwn0lyGZDNgUpGFTOQxPeGuDG6ehTdqx0P9nwt086vpDEhYlA5=+Yha3AqDRExNiGCitBDiDhwB=XDBdA1PCDSfGgKBvnDSiyZeD==; ssxmod_itna2=eu0Q0KAI3x8f40L9YtDymDBQnD7DmEidM0fqA=nIQD/3YDF2dI=E=oKlYzBBaypzH8rTn0FD8xWNz/W9AyDmoP4Yvz8W+PMumqROG390VfKLiTpgbwx38fQvS13=ydvhUGREokTnX7b3fM73E0Dni4=LNedbig7W0gY+IXi+K8n7XQ2CR7b0qp7RUeKGNix=xPzTqfbuc4TpU8lWYyA2EsYmbCSrt++39sIR2+ZQR5v4m3cr=M+rdAs4Sql2fr1zGKWbY4TmIUbQjgQq+IzctvblF1gMFGMzFoM18iBzU70t=M4FkVdHdBrVe7zCwiWvXYYEib//0w7bxDhR+KfIuzUexvFu4Bpf5CUT/AFNlezlxrQWO8hib+B8hL/bl0bQaqQwPr7OMDbGCTs/A5ZGAjnC433D07803eODKExRgzrTE6iKyMEl8+Qa5=Y3G7k208zDsdkLS0L57NOpE7r+gBDHBD8rKfP=GY5=YUp35M0oFxLuoxSZDl7aD7=DYIiXv5m99UxxDhAQ0E42fI3xBe7ta57CH6j5BWxlGwo0msRwOQ2U0eZGaK2Wc0NlGN+lqp=4eD==; accessToken=avatarUrl%3Dhttps%253A%252F%252Fcdn.static.17k.com%252Fuser%252Favatar%252F12%252F32%252F88%252F104288832.jpg-88x88%253Fv%253D1758179303000%26id%3D104288832%26nickname%3DFreedom049527%26e%3D1773732252%26s%3Dbe5b09889cc66379; tfstk=gONiLjDydRk1FpO6IZc1BR9UBvBKffGjdodxDjnVLDoIDNitXrr0ozkq7CrTKmqUxclx65C0nDlZhRnxXmc0lubd2_C85PGj3gIRwip7FfGS7duwDvJErj0aKspP6PGjgMLpgTCu5lO6T9cZgwcEo4oqgIrqLvoIlq-ZuC738Die_dJZQBzE54uq0olV-yoIujo40x738DgqgmkK0Oo_gWPF0SBmKpk0JW0iI0zhFnxqEQHn4PnHggPnSnoz7DA2g0YAkrz3WGA7JfaUq4EOTCquPWZn8u5cxjZQ3ou0Y_tx18E_Nv4CUBzKsDkrzW72amFgpzonZE5a-fmiU5HNZaZz-ry-KWQPNfcZb-h_kUjT-5qT5W4vupli6JDusb5WbmeLrS00Gi13qzNQx2rPiGSzvpJPq1Oj8ZFHhKMZR2m8Pt_7O5QplR7h-L7jQ2gs2wbHhKMZR2mR-wvPlAuI50C..; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22104288832%22%2C%22%24device_id%22%3A%221995b9515327e0-08c385756cd7608-26011051-1327104-1995b9515331c41%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E5%BC%95%E8%8D%90%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22https%3A%2F%2Fgraph.qq.com%2F%22%2C%22%24latest_referrer_host%22%3A%22graph.qq.com%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC%22%7D%2C%22first_id%22%3A%2225c4d053-d289-43e0-87b9-a20d76772fc1%22%7D; Hm_lpvt_9793f42b498361373512340937deb2a0=1758180253"
})
print(resp.text)

        这里其实有两种方法,一种是通过session保存,另一种是登陆后获取其Cookie值进行保存,代码所展示的部分是cookie值登录,而注释掉的则是获取cookie值进行登陆的部分。

五、总结

        看似我们学了很多的爬取方法,觉得我是不是只要一种就可以了?实际却不是这样的,对于不同的网页,所采取的方法也是不一样的,并且要善于利用网络数据包,通过构造header的方式来绕过一些反爬(referer啦,user-agent啦,还有X-forwared-for啦)要让网页误以为你的程序不是程序而是真人,才可以做到爬取想要的数据。

posted @ 2025-09-26 21:06  困困小猫log  阅读(2)  评论(0)    收藏  举报  来源