目录
前言
Python应用的领域有很多,对于爬虫这一块,掌握基本的知识之后再去看相对应的爬虫项目,也会很快上手
1. 基本知识
Python爬虫是一种利用Python编程语言编写的程序,用于从互联网上获取信息
爬虫通常用于网页抓取、数据挖掘、信息监控等领域
以下是Python爬虫的基本知识:
-
HTTP协议与请求发送:
~ HTTP(Hypertext Transfer Protocol)是用于传输超文本的应用层协议,Python爬虫通过发送HTTP请求来获取网页内容
~ 使用Python中的库(如requests、urllib等)可以发送HTTP请求并获取响应 -
HTML解析:
爬虫通常需要从网页中提取信息,因此需要解析HTML文档,使用解析库(如Beautiful Soup、lxml等)可以帮助解析HTML,并提取所需的数据 -
数据存储:
爬虫获取的数据通常需要存储到本地或数据库中供后续处理和分析使用,可以使用文件操作、数据库(如MySQL、MongoDB等)或者其他存储方式来保存数据 -
反爬机制:
~ 网站通常会采取反爬虫措施,如设置验证码、限制IP访问频率等,因此爬虫需要应对这些限制。
~ 使用代理IP、随机User-Agent、降低请求频率等方法可以规避反爬虫策略 -
Robots协议:
~ Robots协议是一种网站使用的标准,用于指导网络爬虫访问网站的行为
~ 使用robots.txt文件来指示哪些页面可以被爬取,哪些页面应该被忽略 -
并发与异步:
大规模爬取需要考虑并发与异步处理,以提高爬取效率。使用多线程、多进程或异步库(如asyncio、aiohttp等)可以实现并发与异步操作 -
定时任务与调度:
爬虫通常需要定时执行,以保持数据的更新。使用定时任务调度工具(如APScheduler、celery等)可以实现定时执行爬虫任务
2. HTTP请求和相应
HTTP请求和响应是Web通信中的基本概念,无论是正常的接口请求还是爬虫请求,它们都使用HTTP协议进行通信
下面是对HTTP请求和响应的详细分析,以及正常接口请求和爬虫请求的区别:
HTTP请求(Request)的组成部分 | HTTP响应(Response)的组成部分 |
---|---|
1.请求行(Request Line):包括请求方法、请求的URL和HTTP协议版本 2.请求头部(Request Headers):包含了客户端向服务器传递的附加信息,如User-Agent、Accept、Cookie等 3.请求体(Request Body):在POST请求中,包含了要发送给服务器的数据 | 1.状态行(Status Line):包括HTTP协议版本、状态码和状态消息 2.响应头部(Response Headers):包含了服务器向客户端传递的附加信息,如Content-Type、Set-Cookie等 3.响应体(Response Body):包含了服务器返回给客户端的数据 |
正常的接口请求和爬虫请求的区别:
方式 | 正常接口 | 爬虫请求 |
---|---|---|
User-Agent | 接口请求通常会使用标准的浏览器User-Agent | 爬虫请求可能会使用自定义的User-Agent,以模拟不同类型的浏览器或设备进行访问 |
请求频率 | 接口请求通常遵循人类的操作频率 | 爬虫请求可能会以更高的频率进行访问,从而可能触发网站的反爬虫机制 |
Cookie | 接口请求可能会携带用户的Cookie信息 | 爬虫请求可能会禁用Cookie或使用自定义的Cookie |
Referer | 接口请求通常会包含Referer字段,指示请求是从哪个页面跳转而来 | 爬虫请求可能会禁用Referer或使用虚假的Referer |
请求方式 | 通常会遵循HTTP协议规范,如使用合适的请求方法(GET、POST等) | 爬虫请求可能会根据需求选择更灵活的请求方式,如使用HEAD请求获取网页头部信息 |
总的来说,正常的接口请求和爬虫请求在使用HTTP协议上没有本质区别,但在一些请求头部信息和请求行的设置上可能会有所不同,主要是为了达到不同的访问目的和需求
2.1 HTTP请求
HTTP请求的组成部分:
-
请求行(Request Line):
包含了HTTP方法、请求的URL和协议版本
格式:<HTTP方法> <URL> <协议版本>
-
请求头部(Request Headers):
包含了请求的各种属性和信息,如User-Agent
、Content-Type
等
每个头部以键值对的形式出现,中间用冒号分隔
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
Content-Type: application/json
- 请求体(Request Body):
仅在使用POST、PUT等方法时出现,用于传递请求参数或数据
请求体的格式可以是文本、JSON、XML等
2.2 HTTP响应
HTTP响应的组成部分:
-
状态行(Status Line):
包含了响应的状态码和对应的描述
格式:<协议版本> <状态码> <状态码描述>
-
响应头部(Response Headers):
包含了服务器对请求的响应信息,如Content-Type
、Content-Length
等
格式与请求头部相同,每个头部以键值对的形式出现 -
响应体(Response Body):
包含了服务器返回的实际数据,可以是HTML、JSON、XML等格式的文本
整体的请求示例:
GET /api/data HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
Accept: application/json
整体的相应示例:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 56
{
"status": "success",
"data": {
"message": "Data retrieved successfully"
}
}
3. request库
在Python中,爬虫常常使用requests库来发送HTTP请求
requests是一个简洁而强大的HTTP库,使得发送HTTP请求变得容易,并能够方便地处理响应数据
以下是对requests库的概念以及一些示例的详细分析:
-
HTTP请求发送:requests库允许通过简单的API发送各种类型的HTTP请求,如GET、POST、PUT、DELETE等
-
请求参数设置:可以通过参数设置请求的各种属性,如headers、params、data、cookies等,以满足不同的需求
-
响应处理:requests库可以方便地处理HTTP响应,包括状态码、响应头、响应体等
使用之前需要安装:pip install requests
发送GET请求并获取响应:
import requests
url = 'https://jsonplaceholder.typicode.com/posts/1'
response = requests.get(url)
print(response.status_code) # 打印状态码
print(response.headers) # 打印响应头部
print(response.text) # 打印响应体的文本内容
截图如下:
发送带参数的GET请求:
import requests
url = 'https://jsonplaceholder.typicode.com/posts'
params = {'userId': 1}
response = requests.get(url, params=params)
print(response.status_code)
print(response.json()) # 解析JSON格式的响应体
发送POST请求并传递数据:
import requests
url = 'https://jsonplaceholder.typicode.com/posts'
data = {'title': 'foo', 'body': 'bar', 'userId': 1}
response = requests.post(url, json=data)
print(response.status_code)
print(response.json())
截图如下:
设置请求头部和cookies:
import requests
url = 'https://httpbin.org/headers'
headers = {'User-Agent': 'Mozilla/5.0'}
cookies = {'session_id': '123456789'}
response = requests.get(url, headers=headers, cookies=cookies)
print(response.json())
处理异常:
import requests
url = 'https://nonexistent-domain.com'
try:
response = requests.get(url)
response.raise_for_status() # 如果响应状态码不是200,则抛出异常
except requests.exceptions.RequestException as e:
print("Error:", e)
截图如下:
实战演示
为了爬取豆瓣250的电影:
import requests
response = requests.get("https://movie.douban.com/top250")
print(response.status_code)
结果返回418,这代表加入了反爬
为了让网站显示这是浏览器的请求,需要增加请求头,对应在自身浏览器中刷新并按F12,查看自身请求头
import requests
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"
}
response = requests.get("https://movie.douban.com/top250",headers = headers)
print(response.status_code)
最终结果返回200,代表爬取成功
对应response.text
为网页信息,如何解析对应的html文件呢??
4. HTML解析
此处着重讲一个Beautiful Soup
库
先安装:pip install Bs4
Beautiful Soup
是Python中一个用于解析HTML和XML文档的库,它能够从网页中提取数据,方便地进行信息抽取和数据挖掘
有一些常用的API能够帮助你查找、遍历和提取所需的信息
常用API | 具体描述 |
---|---|
BeautifulSoup() | 用于初始化Beautiful Soup对象,传入待解析的HTML或XML文档以及解析器类型 |
find() / find_all() | 用于查找符合条件的单个元素或多个元素 |
select() | 使用CSS选择器来查找元素 |
get_text() | 获取元素的文本内容 |
find_parent() / find_parents() | 查找元素的父元素或所有父元素 |
find_next_sibling() / find_next_siblings() | 查找元素的下一个兄弟元素或所有下一个兄弟元素 |
以下是对一些常用API的详细分析,并附带了相应的小Demo示例:
4.1 find() 和 find_all()
find
函数:用于查找第一个匹配的元素
find(name, attrs, recursive, string, **kwargs):
参数:
name
:要查找的标签名或标签名列表attrs
:要匹配的属性或属性字典recursive
:是否递归查找,默认为Truestring
:要匹配的文本内容**kwargs
:其他参数
返回值:找到的第一个匹配的元素,如果没有找到则返回None
find_all
函数:用于查找所有匹配的元素
find_all(name, attrs, recursive, string, limit, **kwargs):
参数:与find()类似,但多了一个limit
参数,用于限制返回结果的数量
返回值:匹配的元素列表,如果没有找到则返回空列表
示例Demo:
from bs4 import BeautifulSoup
# HTML示例
html_doc = """
<html><head><title>示例文档</title></head>
<body>
<p class="title"><b>标题</b></p>
<p class="content">内容1</p>
<p class="content">内容2</p>
</body></html>
"""
# 创建Beautiful Soup对象
soup = BeautifulSoup(html_doc, 'html.parser')
# 使用find()查找第一个匹配的元素
first_content = soup.find('p', class_='content')
print("第一个内容元素:", first_content)
# 使用find_all()查找所有匹配的元素
all_content = soup.find_all('p', class_='content')
print("所有内容元素:", all_content)
截图如下:
4.2 get_text()
用于获取元素内部的文本内容
get_text(separator, strip, types)
参数:
separator
:文本分隔符,默认为换行符strip
:是否去除文本两端的空白字符,默认为Truetypes
:指定返回文本的类型,默认为字符串
返回值:元素内部的文本内容
截图如下:
4.3 select()
用于使用CSS选择器来选择元素
select(css_selector)
参数:css_selector
:CSS选择器字符串
返回值:匹配的元素列表
from bs4 import BeautifulSoup
# HTML示例
html_doc = """
<html><body>
<div id="container">
<ul class="list">
<li>项目1</li>
<li>项目2</li>
<li>项目3</li>
</ul>
</div>
</body></html>
"""
# 创建Beautiful Soup对象
soup = BeautifulSoup(html_doc, 'html.parser')
# 使用select()选择元素
items = soup.select('#container .list li')
for item in items:
print("项目:", item.get_text())
截图如下:
5. 实战
5.1 Demo1
使用Beautiful Soup
库爬取豆瓣电影Top250页面的信息
首先发送HTTP请求获取网页内容,然后使用Beautiful Soup
解析HTML内容,查找所有电影条目,并提取其中的电影标题、评分和评论信息,最后打印出提取的电影信息
import requests
from bs4 import BeautifulSoup
# 发送HTTP请求并获取网页内容
url = 'https://movie.douban.com/top250'
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"
}
response = requests.get(url,headers = headers)
html_content = response.text
# 使用Beautiful Soup解析网页内容
soup = BeautifulSoup(html_content, 'html.parser')
# 查找所有电影条目
movie_items = soup.find_all('div', class_='info')
# 提取电影信息并打印
for item in movie_items:
title = item.find('span', class_='title').text.strip()
rating = item.find('span', class_='rating_num').text.strip()
quote_elem = item.find('span', class_='inq')
quote = quote_elem.text.strip() if quote_elem else '暂无评价'
print(f'电影:{title}\n评分:{rating}\n评论:{quote}\n')
截图如下:(此处只打印了25个,毕竟每一页都是25条数据)
5.2 Demo2
获取所有电影的电影名
import requests
from bs4 import BeautifulSoup
# 发送HTTP请求并获取网页内容
url = 'https://movie.douban.com/top250'
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"
}
# 加入分页的参数
for start_num in range(0, 250, 25):
response = requests.get(f"{url}?start={start_num}", headers=headers) # 将"url"替换为实际的URL
html = response.text
soup = BeautifulSoup(html, 'html.parser')
all_titles = soup.find_all("span", attrs={"class": "title"}) # 修改"sapn"为"span"
for title in all_titles:
title_string = title.string
if "/" not in title_string:
print(title_string)
截图如下: