Python可以高效开发网络爬虫,可用于信息搜集、数据分析、网站投票等功能。

文章介绍Python网络爬虫的基础概念和常用模块,爬虫实例参考文章:Python爬虫实例

一、基础概念

(一) 定义

网络爬虫,是一种按照一定规则,通过模拟客户端发送网络请求和接收响应,自动抓取网页信息的程序。只要是浏览器能做的事情,原则上爬虫都可以做。

我们常用的搜索引擎就是一个大规模的爬虫,获取的海量信息存储在数据库,当用户输入关键字进行查询时,搜索引擎通过索引快速返回结果。

(二) 分类

1.通用爬虫

通常指搜索引擎爬虫,基本工作原理:种子url列表抓取网页—数据存储—预处理提取关键信息—提供检索服务—网站排名(根据引用次数、点击次数等)

2.聚焦爬虫

针对特定网站的爬虫,基本工作原理:url列表—响应内容—提取url—提取数据—数据存储

当前页面的数据来源主要有三种:

  • 对当前url请求对应的响应中

  • 对其他url请求对应的响应中,比如ajax请求

  • 响应中的js脚本动态生成

只有第一种数据才可以通过爬虫获取,因为当前url地址呈现的内容和url请求直接获取的响应不一定相同,响应中可能会通过js动态加载一些图片、样式,而当前页面呈现的是这些内容加载后的结果。

二、常用模块

爬虫核心功能即模拟客户端行为发送请求,然后从响应信息中提取数据。发送请求过程最常用到的模块为 requests,提取数据过程可以根据网页结构和属性提取,使用模块 BeautifulSoup,也可以通过正则表达式提取,使用模块 re

(一) requests

1.基本使用

Python通用网络爬虫可以完成爬虫90%的工作,主要涉及requests模块的使用,requests的底层实现就是urllib,而且在Python2和Python3中通用。

requests对象的方法主要包括:request、get、post、head等,具体如下:

方法含义类型
request(method, url, **kwargs)发送http请求,具体方法由method指定Response
get(url, **kwargs)发送http get方法请求Response
post(url, **kwargs)发送http post方法请求Response
head(url, **kwargs)发送http head方法请求Response

方法中url为访问的网页url,**kwargs为控制访问的参数,共13个:

  • headers:请求头
  • params:请求参数
  • data:请求数据
  • cookies:携带cookies
  • json:JSON格式的请求数据
  • files:传输文件
  • timeout:请求超时时间,单位为秒
  • proxies:请求代理
  • allow_redirects:重定向,值为bool类型,默认为True
  • stream:获取内容立即下载,值为bool类型,默认为True
  • verify:认证SSL证书,值为bool类型,默认为True
  • cert:本地SSL证书
  • auth:支持HTTP认证

Response对象的常用属性:

属性含义类型
textcontent经过解码的数据str
contentResponse的原始byte数据,没有经过解码bytes
status_code返回请求状态码int
headersResponse头信息dict
request.xxx返回对应的请求的方法和属性

注意:

  • response.text默认是ISO-8859-1编码 (可通过response.encoding查看),显示内容可能出现乱码,如果需要指定编码需要使用response.encoding="utf-8"指定,或者使用response.content.decode(“utf-8”),默认utf-8解码
  • 网页返回一整行json格式数据时,可以利用pycharm中Code-> Code Reformat Code,或者使用BeautifulSoup对象的prettify方法调整格式,使代码更易读
  • response.request.url与response.url不一定相同,比如重定向网站
  • request请求包中headers中User-Agent默认是python-requests/xxxxx,这导致很多网站返回结果与普通浏览器访问的不同,因此有必要在headers中模拟浏览器重新指定User-Agent
  • 爬虫要模仿浏览器的合法请求,并不是所有请求参数都是必须的,要尝试、筛选使浏览器返回正常页面的必要参数
  • 如果电脑版请求不易仿造,可以尝试手机版
  • 保存图片、文件、音频、视频等要以二进制形式写入:
response = requests.get("https://img-blog.csdnimg.cn/2021041421411836.png")

# 以二进制形式写入
with open("test.jpg", "wb") as f:
    f.write(response.content)
    f.close()
2.使用代理

区分正向代理和反向代理:

(1) 正向代理

客户端知道目标服务器地址,只是在中间设置代理机

(2) 反向代理

客户端不知道服务器地址,目标服务器由代理进行访问

使用方法:

proxies = {
    "http": "http://xx.xx.xx.xx:port",
    "https":"https://ip:port"
    }

使用代理作用:

  • 让服务器以为不是同一个客户端在请求

  • 防止真实地址被泄露,防溯源

免费代理网站,适用于时限要求不长、质量要求不高的用户:

http://proxy.mimvp.com
http://www.66ip.cn/index.html
https://ip.jiangxianli.com/
http://www.ip3366.net/free/?stype=3
https://ip.ihuan.me/
https://www.kuaidaili.com/free/inha/
https://www.7yip.cn/free/

为判断代理的可用性,可以在requests请求中添加超时参数进行测试,一般准备一堆ip地址组成ip池,随机选择使用。

3.模拟登陆

网站验证机制主要包括Session、Cookie和Token,具体区别参考文章:浅析Session、Cookie、Token原理与区别

模拟登录首先要利用浏览器开发者工具,通过查看源码或者抓包分析,确定网站的登录地址,然后利用爬虫仿照正常登录发送的请求进行登录。

(1) 确定登录地址

思路一:查看源码

在前端源码中寻找提交表单action对应的url地址,input标签中name的值作为键,用户名和密码作为值。

思路二:抓包分析

通过抓包寻找登录的url地址,如果提交的参数不会变,则直接用,如果参数动态变化,则要进一步确定生成参数的方式,注意参数生成可以通过当前url的响应,也可以通过js脚本。

(2) 模拟登陆网站

思路一:利用session类

requests提供了一个session类,专门用来实现客户端和服务端会话保持,使用方式:

# 实例化session对象
session = requests.session()
# 登录网址,其中data中有登录信息
session.post(url, data=data)
# 发送请求,session自动携带登录成功的凭证信息
response = session.get(url, headers)

在成功登录后,之后的访问session能够自动携带登录成功时的凭证,通常是cookie。

思路二:携带凭证登陆

这里以cookie为例,可以通过POST登录请求,定位response响应信息中的Set-Cookie头中找到cookie信息,或者从已登录界面Cookie头中查看cookie信息。登录请求中携带cookie的方法一般有两种:

方法一:cookie放在headers中携带

headers={
		 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0",
         "cookies":"xxx"
         }

requests.get(url, headers=headers)

方法二:cookie放在requests请求参数中携带

使用字典推导式将cookie字符串转换成字典格式:

# 字符串格式
cookies = "_r01=1; _ga=1.12213231; login=y8a7d8y87c87a; JSESSIONID=ay89d78aef8t9"
# 字典格式
cookies = {i.split("=")[0]:i.split("=")[1] for i in cookies.split("; ")}

requests.get(url, headers=headers, cookies=cookies)

注意:不需要使用cookies时尽可能不用,不然容易被识别为爬虫。

(二) BeautifulSoup

BeautifulSoup可以根据HTML网页的结构和属性查找所需信息,它利用解析器将HTML或XML文档转换成一个树形结构,解析器包括以下几种:

  • Python标准库解析器,Python内置标准库,无需安装,使用方法:BeautifulSoup(markup, "html.parser")
  • lxml HTML解析器,需要安装,使用方法:BeautifulSoup(markup, "lxml")
  • lxml XML解析器,需要安装,唯一支持XML的解析器,使用方法:BeautifulSoup(markup, "xml")
  • html5lib解析器,需要安装,使用方法:BeautifulSoup(markup, "html5lib")

树形结构中每个节点都是一个对象,所有对象可以归纳为4种:

1.BeautifulSoup

BeautifulSoup对象以结构化记录着HTML文档的所有内容,BeautifulSoup(html, "html.parser")返回的即BeautifulSoup对象,常见属性/方法如下:

属性 / 方法含义类型
<TagName>获取第一个TagName标签的内容,返回Tag对象Tag
prettify()格式化BeautifulSoup对象的内容,增加易读性Str
find_all(name[, key = value | {key: value}])通过标签名称和属性筛选查找Tag对象,返回标签对象列表List[Tag]
find(name[, key = value | {key: value}])通过标签名称和属性筛选查找Tag对象,返回符合条件的第一个对象Tag

2.Tag
Tag对象即HTML文档中的标签,常见属性/方法如下:

属性 / 方法含义类型
name获取标签的名字Str
attrs获取标签所有的属性,通过attrs[name]获取属性的值Dict
string获取标签内容,返回NavigableString对象NavigableString
contents获取直接子节点标签,包括NavigableString和换行符,返回Tag / NavigableString对象列表List[Tag / NavigableString]
children获取子孙节点标签,包括NavigableString和换行符,返回列表生成器List[Generator]
parent获取直接父节点标签,返回Tag对象Tag
parents获取祖先节点标签,返回生成器Generator
get(name)获取标签相应属性的值的列表,也可以使用attrs[name]获取List

3.NavigableString
NavigableString对象即Tag标签内的内容

4.Comment
Comment对象即Tag标签内的注释内容,不包括注释符

安装方法:pip3 install beautifulsoup4,安装之后的包名为bs4,一般只导入BeautifulSoup,解析器使用"html.parser",具体使用方法如下例:

from bs4 import BeautifulSoup

html = """
<html>
    <head>
        <title>Demo Introduction</title>
    </head>
    <body>
        <p class="comment"> 
            <!-- 高中介绍 --> 
        </p>
        <p class="Junior">
            The top junior high.
            <a href="http://xxx.com" class="addr" id="1">
                <span>ABCD</span>
            </a>
            <a href="http://yyy.com" class="addr" id="2">
                <span>EFGH</span>
            </a> 
        </p>
        <p class="comment"> 
            <!-- 大学介绍 -->
        </p>
        <p class="College">
            The top college.
            ...
        </p>
</html>
"""

soup = BeautifulSoup(html, "html.parser")

print(soup.a)
print(soup.find("a"))
# result: <a class="addr" href="http://xxx.com" id="1"> <span>ABCD</span> </a>

print(soup.a.string)
# result: None

print(soup.a.contents[1].string)
# result: ABCD

print(soup.a.attrs)
# result: {'href': 'http://xxx.com', 'class': ['addr'], 'id': '1'}

print(soup.span.string)
# result: ABCD

print(soup.find_all("span"))
# result: [<span>ABCD</span>, <span>EFGH</span>]

print(soup.find("span"))
# result: <span>ABCD</span>

print(soup.find("a", id="2"))
print(soup.find("a", {"id": "2"}))
# result: <a class="addr" href="http://yyy.com" id="2"> <span>EFGH</span> </a>

(三) re

参考文章:Python文件操作

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐