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对象的常用属性:
属性 | 含义 | 类型 |
---|---|---|
text | content经过解码的数据 | str |
content | Response的原始byte数据,没有经过解码 | bytes |
status_code | 返回请求状态码 | int |
headers | Response头信息 | 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文件操作