目录
requests
对网站模拟发起请求库
Response对象
参数
python
import requests
url = "https://www.jd.com"
r = requests.get(url)
r.encoding = r.apparent_encoding # 设置使用的编码和解析出来的方法一致
print(r.status_code) # 获取响应状态码
print(r.url) # 获取请求的url
print(r.headers) # 获取响应头
print(r.text) # 获取经过编码后的响应文本内容
print(r.content) # 获取未经编码的响应文本内容(二进制数据)
print(r.encoding) # http响应头中的编码字段,text方法根据该值进行解码,若为空则默认为ISO-8859-1
print(r.apparent_encoding) # 根据响应内容,解析出来的编码
print(r.cookies) # 获取cookie
print(r.request.headers) # 获取发送请求的请求头
session = request.session() # 获取一个会话
get&post提交
post
python
import requests
uname = 'G0od!JAVA3C41PTISAGO'
upass = '1pt_Pa4sW0rd_K3y_H3re'
url = 'http://172.29.1.2/Ch3ck_Au7h.php'
para = { # 提交参数为字典
'uname': uname,
'upass': upass
}
r = requests.post(url=url,data=para)
print(r.text)
定制请求参数
python
import requests
# 自定义请求头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0"
}
# 自定义参数
para = {
"key": "羊毛"
}
url = "https://www.jd.com/"
r = requests.get(url,headers=headers,params=para) # 对url发起get请求,并设置请求头和get参数
r.encoding = r.apparent_encoding # 设置使用的编码和解析出来的方法一致
print(r.text)
requests.request用法
python
res = requests.request('GET', url, params=None)
# 方法 url 控制访问参数
# 控制访问参数:
# params 字典或字节序列 作为参数增加到url中 get常用
# data 字典、字节序列或文件对象 作为request的内容 post常用
# json JSON格式的数据 作为request的内容
# headers 字典 http定制头
# cookies 字典或CookieJar 作为request的cookie
# auth 元组 作为http认证
# files 字典 传输文件 键为文件名,值为open函数打开的文件
# timeout 设置超时时间(单位为秒)
# allow_redirects True/False 默认为true 重定向开关
# stream True/False 默认为true 获取内容自动下载开关
# verify True/False 默认为true 认证SSL证书开关
# cert 使用本地SSL证书路径
通用框架
python
import requests
def getHTMLText(url):
try:
r = requests.get(url)
r.raise_for_status() # 如果状态不是200,引发HTTPError异常
r.encoding = r.apparent_encoding # 根据编码内容设置编码格式
return r.text
except:
return "产生异常"
if __name__ == "__main__":
url = 'http://www.baidu.com'
print(getHTMLText(url))
BeautifulSoup
创建解析对象
python
import requests
from bs4 import BeautifulSoup # 导入beautifulsoup库
url = "https://www.jd.com/"
r = requests.get(url) # 对url发起get请求,并设置请求头和get参数
r.encoding = r.apparent_encoding # 设置使用的编码和解析出来的方法一致
h = r.text
soup = BeautifulSoup(h,features='html.parser') # 创建对象(可以是字符串作为参数,也可以使用open('test.html')打开html文件) 也叫封装为文档树
解析标签
通过属性的方式获取内容,包含标签本身
python
# 获取标签
soup.a # 获取第一个a标签所有内容
soup.title
soup.head
soup.body
soup.img
soup.div
.......
# 格式化输出
soup.prettify()
# 过滤方法
soup.div['id'] # 获取div标签的第一个id的值
# 基本元素属性
soup.a.name # 获取标签的名字
soup.a.parent.name # 获取父标签的名字
soup.a.attrs # 获取标签所有的属性 返回一个字典
soup.a.contents # 获取标签的文本 返回一个列表 以标签分割 包括换行
soup.a.string # 获取标签的直接文本 返回一个字符串
soup.a.text # 获取标签内所有的文本 返回一个字符串
标签遍历
python
# 遍历标签下的所有子标签
for child in soup.div.children:
print(child)
# 遍历标签的所有父标签
for parent in soup.div.parents:
print(parent)
搜索文档树
python
# find_all() 返回所有匹配标签节点的列表
soup.find_all(name=True) # 获取所有标签
soup.find_all('a') # 字符串搜索 获取所有a标签
soup.find_all(re.compile('a')) # 正则搜索
soup.find_all(["meta","link"]) # 列表内容搜索
soup.find_all(id='link') # 获取id=link的所有标签,支持任意标签查找
soup.find_all(id=True) # 获取有id属性的标签
soup.find_all(class_='test') # 获取class为test的标签,class_与class区分
soup.find_all(string='test') # 获取文本内容有test的标签 返回文本信息列表
#选择多个用列表,如class_=['test1','test2']
# find()返回第一个匹配的标签节点
# 用法与find_all()一致
# select() 返回所有匹配的标签节点列表 (css选择器)
soup.select('a') # 获取所有的a标签
soup.select('p a') # 获取p标签下的a所有标签
soup.select('p>a') # 获取p标签下的直接子节点的a所有标签
soup.select('.test') # 获取class属性值为test的所有标签
soup.select('a.test') # 获取a标签中class属性值为test的所有标签
soup.select('.a1.a2') # 获取class属性值为a1的标签中查找class属性值为a2的所有标签
soup.select('#test') # 获取id为test的所有标签
soup.select('p #test') # 获取p标签下的id为test的所有标签
soup.select('a[href]') # 获取a标签中存在href属性的所有标签
soup.select('a[href="www.baidu.com"]') # 获取a标签中属性为www.baidu.com所有的标签
# 多个用,隔开
lxml
解析html
选择解析方式
python
# 1.从字符串解析
from lxml import etree
html = """
<html>
<body>
<div class="content">
<h1>标题</h1>
<p>段落文本</p>
</div>
</body>
</html>
"""
tree = etree.HTML(html)
# 2.从文件中解析
# 方法1
with open('example.html', 'r', encoding='utf-8') as f:
tree = etree.HTML(f.read())
# 方法2
tree = etree.parse('example.html', etree.HTMLParser())
xpath
基本选择器
| 符号 | 描述 | 示例 | 匹配结果 |
|---|---|---|---|
/ |
从根节点开始的绝对路径 | /html/body/div |
根节点下的 <div> |
// |
任意层级的节点(全局搜索) | //div |
文档中的所有 <div> |
. |
当前节点 | ./span |
当前节点下的直接 <span> |
.. |
当前节点的父节点 | ../li |
当前节点的父级 <li> |
@ |
选择属性 | //a/@href |
所有 <a> 标签的 href 属性 |
谓语过滤
| 示例 | 描述 |
|---|---|
//li[1] |
第一个 <li> 节点(XPath 索引从 1 开始) |
//li[last()] |
最后一个 <li> 节点 |
//li[last()-1] |
倒数第二个 <li> 节点 |
//li[@class="item"] |
所有 class="item" 的 <li> 节点 |
//li[a] |
包含 <a> 子节点的 <li> 节点 |
//li[text()="item"] |
文本内容为 "item" 的 <li> 节点 |
通配符
| 通配符 | 描述 | 示例 | 匹配结果 |
|---|---|---|---|
* |
任意元素节点 | //div/* |
所有 <div> 的直接子节点 |
@* |
任意属性 | //a/@* |
所有 <a> 标签的所有属性 |
node() |
任意类型的节点 | //div/node() |
<div> 内的所有节点(包括文本、注释等) |
轴
| 轴名称 | 描述 | 示例 | 匹配结果 |
|---|---|---|---|
child |
当前节点的直接子节点 | //div/child::p |
<div> 的直接子 <p> |
descendant |
当前节点的所有后代节点 | //div/descendant::p |
<div> 内的所有 <p>(无论嵌套多深) |
parent |
当前节点的父节点 | //p/parent::div |
<p> 的父级 <div> |
ancestor |
当前节点的所有祖先节点 | //span/ancestor::div |
<span> 的所有 <div> 祖先 |
following |
当前节点之后的所有节点 | //li/following::li |
当前 <li> 之后的所有 <li> |
preceding |
当前节点之前的所有节点 | //li/preceding::li |
当前 <li> 之前的所有 <li> |
attribute |
当前节点的属性 | //a/attribute::href |
<a> 的 href 属性 |
运算符
| 运算符 | 描述 | 示例 |
|---|---|---|
and |
逻辑与 | //li[@class="item" and @id] |
or |
逻辑或 | //li[@class="item" or @id] |
= |
等于 | //a[@href="link.html"] |
!= |
不等于 | //a[@href!="link.html"] |
> |
大于 | //li[position() > 3] |
函数
| 函数名 | 描述 | 示例 | 结果 |
|---|---|---|---|
text() |
获取节点的文本内容 | //a/text() |
<a> 标签的文本内容 |
contains() |
判断属性或文本是否包含特定值 | //div[contains(@class, "box")] |
所有 class 包含 "box" 的 <div> |
starts-with() |
判断属性或文本是否以特定值开头 | //a[starts-with(@href, "http")] |
所有 href 以 "http" 开头的 <a> |
position() |
获取节点在同级中的位置 | //li[position() <= 3] |
前三个 <li> 节点 |
last() |
获取最后一个节点的位置 | //li[last()] |
最后一个 <li> 节点 |
使用方法
python
import requests
from lxml import etree
url = "http://www.baidu.com"
req = requests.get(url)
req.encoding = req.apparent_encoding
tree = etree.HTML(req.text) # 创建一个etree对象
elem = tree.xpath("//*[@class='title-content-title']/text()") # 获取所有节点下指定的class值的文本信息 返回列表
print(elem)
Selenium
配置驱动
python
1. 下载对应浏览器的webdriver
EdgeDriver:https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
ChromeDriver:https://googlechromelabs.github.io/chrome-for-testing/#stable
2. 将exe放入python安装目录的script目录下
3. 配置系统环境变量
页面操作
python
from selenium import webdriver
option = webdriver.EdgeOptions() # 初始化edge浏览器配置对象
option.add_experimental_option('detach', True) # 配置不自动关闭
edge = webdriver.Edge(options=option) # 初始化edge浏览器对象并使用配置
# 窗口大小设置
edge.maximize_window() # 全屏窗口
edge.set_window_size(500,500) # 设置大小
# 前进与后退
edge.forward() # 前进
edge.back() # 后退
edge.get("https://taobao.com") # 自动化打开一个网页
# 页面关闭
edge.close() # 关闭当前页
edge.quit() # 关闭整个浏览器
获取页面属性
python
from selenium import webdriver
option = webdriver.EdgeOptions() # 初始化edge浏览器配置对象
option.add_experimental_option('detach', True) # 配置不自动关闭
edge = webdriver.Edge(options=option) # 初始化edge浏览器对象并使用配置
edge.get("http://baidu.com")
print(edge.title) # 获取页面标题
print(edge.current_url) # 获取当前URL
print(edge.name) # 获取浏览器名称
print(edge.page_source) # 获取页面源码
定位元素
定位方法
python
find_element() # 返回第一个匹配的对象
find_elements() # 返回所有匹配的对象列表
| 属性 | 用法 |
|---|---|
| CLASS | find_element(by=By.CLASS_NAME, value='') |
| XPATH | find_element(by=By.XPATH, value='') |
| CSS | find_element(by=By.CSS_SELECTOR, value='') |
| ID | find_element(by=By.ID, value='') |
python
# xpath定位方法
from selenium import webdriver
from selenium.webdriver.common.by import By # 导入定位库
# 初始化配置
edge = webdriver.Edge()
edge.get("http://baidu.com")
# 关键字传参要么都写要么都不写
elem = edge.find_element(by=By.XPATH, value='//span[@class="title-content-title"]')
print(elem.text)
鼠标模拟操作
| 操作 | 函数 |
|---|---|
| 右击 | context_click() |
| 双击 | double_click() |
| 拖拽 | double_and_drop() |
| 悬停 | move_to_element() |
| 执行 | perform() |
python
from selenium import webdriver
from selenium.webdriver.common.by import By # 导入定位库
# 初始化配置
option = webdriver.EdgeOptions()
option.add_experimental_option("detach", True)
edge = webdriver.Edge(options=option)
edge.get("http://baidu.com")
# 选中元素
elem = edge.find_element(By.XPATH, '//span[@class="title-content-title"]')
elem.click() # 单击
键盘模拟操作
| 操作 | 函数 |
|---|---|
| 删除键 | send_keys(Keys.BACK_SPACE) |
| 空格键 | send_keys(Keys.SPACE) |
| 制表键 | send_keys(Keys.TAB) |
| 回退键 | send_keys(Keys.ESCAPE) |
| 回车 | send_keys(Keys.ENTER) |
| 全选 | send_keys(Keys.CONTRL,'a') |
| 复制 | send_keys(Keys.CONTRL,'c') |
| 剪切 | send_keys(Keys.CONTRL,'x') |
| F1键 | send_keys(Keys.F1) |
| 输入a | send_keys(Keys.INSERT,'a') |
| 直接传值 | send_keys('a') |
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys # 导入键盘库
# 初始化配置
option = webdriver.EdgeOptions()
option.add_experimental_option("detach", True)
edge = webdriver.Edge(options=option)
edge.get("http://baidu.com")
elem = edge.find_element(By.XPATH, '//input[@class="s_ipt"]') # 选择输入框
elem.send_keys(Keys.INSERT,'a') # 输入a
elem.send_keys(Keys.ENTER) # 回车
等待
如果遇到使用ajax加载的网页,页面元素可能不是同时加载出来的,这个时候尝试在get方法执行完成时获取网页源代码可能并非浏览器完全加载完成的页面。所以,这种情况下需要设置延时等待一定时间,确保全部节点都加载出来
| 等待方式 | 方法 | 描述 |
|---|---|---|
| 强制等待 | time.sleep(n) | 强制等待n秒,使用time模块的time.sleep() |
| 隐式等待 | implicitly_wait(n) | 如果到时间有元素节点没有加载出来,就会抛出异常 |
| 显式等待 | WebDriverWait() | 设置一个等待时间和一个条件,在规定时间内,每隔一段时间查看下条件是否成立,如果成立那么程序就继续执行,否则就抛出一个超时异常。 |
python
# 显示等待函数参数
driver: 浏览器驱动
timeout: 超时时间,等待的最长时间(同时要考虑隐性等待时间)
poll_frequency: 每次检测的间隔时间,默认是0.5秒
ignored_exceptions:超时后的异常信息,默认情况下抛出NoSuchElementException异常
until(method,message='')
method: 在等待期间,每隔一段时间调用这个传入的方法,直到返回值不是False
message: 如果超时,抛出TimeoutException,将message传入异常
until_not(method,message=''): 与until相反,until是当某元素出现或什么条件成立则继续执行,until_not是当某元素消失或什么条件不成立则继续执行,参数也相同。
# 显示等待
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as ec # 导入监测条件库
from selenium.webdriver.support.wait import WebDriverWait # 导入显示等待库
# 初始化配置
option = webdriver.EdgeOptions()
option.add_experimental_option("detach", True)
edge = webdriver.Edge(options=option)
edge.get("http://baidu.com")
elem = edge.find_element(By.XPATH, '//input[@class="s_ipt"]') # 选择输入框
try:
# 表示在20s内每0.5s监测一次
wait = WebDriverWait(edge, 20, 0.5) # 获取一个显示等待对象
elem.send_keys(Keys.INSERT, 'a') # 输入a
wait.until(ec.element_to_be_clickable(elem)) # 监测条件
elem.send_keys(Keys.ENTER) # 回车
except:
pass
'''
from selenium.webdriver.support import expected_conditions as EC
# 判断标题是否和预期的一致
title_is
# 判断标题中是否包含预期的字符串
title_contains
# 判断指定元素是否加载出来
presence_of_element_located
ex: wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='haha']")))
# 判断所有元素是否加载完成
presence_of_all_elements_located
# 判断某个元素是否可见. 可见代表元素非隐藏,并且元素的宽和高都不等于0,传入参数是元组类型的locator
visibility_of_element_located
# 判断元素是否可见,传入参数是定位后的元素WebElement
visibility_of
# 判断某个元素是否不可见,或是否不存在于DOM树
invisibility_of_element_located
# 判断元素的 text 是否包含预期字符串
text_to_be_present_in_element
# 判断元素的 value 是否包含预期字符串
text_to_be_present_in_element_value
#判断frame是否可切入,可传入locator元组或者直接传入定位方式:id、name、index或WebElement
frame_to_be_available_and_switch_to_it
#判断是否有alert出现
alert_is_present
#判断元素是否可点击
element_to_be_clickable
# 判断元素是否被选中,一般用在下拉列表,传入WebElement对象
element_to_be_selected
# 判断元素是否被选中
element_located_to_be_selected
# 判断元素的选中状态是否和预期一致,传入参数:定位后的元素,相等返回True,否则返回False
element_selection_state_to_be
# 判断元素的选中状态是否和预期一致,传入参数:元素的定位,相等返回True,否则返回False
element_located_selection_state_to_be
#判断一个元素是否仍在DOM中,传入WebElement对象,可以判断页面是否刷新了
staleness_of
反爬策略
代理
python
import requests
url = "https://www.ptpress.com.cn"
# 定义代理ip池
proxies = [
{'http': 'http://119.28.51.117:80'},
{'http': 'http://112.213.97.68:80'},
{'http': 'http://119.28.188.102:80'}
]
for proxy in proxies:
try:
response = requests.get(url, proxies=proxy)
except:
print("此代理IP不可用")
else:
print("此代理IP可用", proxy)
print("响应状态码:", response.status_code)
随机延迟
python
import requests
import random
import time
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0"
}
for i in range(0,250,25):
url = f"https://movie.douban.com/top250?start={i}&filter="
req = requests.get(url, headers=header)
sleep_time = random.randint(0,2) + random.random()
time.sleep(sleep_time) # 随机等待延迟
验证码处理
图片验证码
pytesseract
需要预先安装tesseract-ocr.exe,python中将pytesseract的tesseract_cmd参数修改为exe路径
python
# 识别思路
1. 获取验证码图片
2. 将图片转为灰度图
3. 将图片转为二值图
4. 用OCR技术识别(光学字符识别)
import pytesseract
from io import BytesIO
session = requests.session()
url = "http://www.daimg.com/include/vdimgck.php"
r = session.get(url)
img = Image.open(BytesIO(r.content))
code = pytesseract.image_to_string(img) # 识别图像文字
print(code)
# 实例
from bs4 import BeautifulSoup
import requests
from PIL import Image
import pytesseract
from io import BytesIO
import re
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0"
}
url = "http://www.daimg.com/member/login.php"
session = requests.session()
session.headers = header
req = session.get(url)
req.encoding = req.apparent_encoding
soup = BeautifulSoup(req.text, 'html.parser')
elem = soup.select("#vdimgck", )[0]['src']
url = "http://www.daimg.com/" + elem[3:]
r = session.get(url)
img = Image.open(BytesIO(r.content))
code = re.findall(r'\w+',pytesseract.image_to_string(img))[0]
print("原图识别:", code)
img = img.convert('L') # 将图像转成灰度图,L为灰度图,1为二值图(table,'1')
code = re.findall(r'\w+',pytesseract.image_to_string(img))[0]
print("灰度图识别:", code)
table = [] # 定义二值化列表
th = 127 # 定义二值化阈值
# 将像素小于阈值的填充0(黑色),大于填充1(白色)
for i in range(256):
if i < th:
table.append(0)
else:
table.append(1)
img = img.point(table, '1') # 二值化,1为二值图
code = re.findall(r'\w+',pytesseract.image_to_string(img))[0]
print("二值图识别:", code)
img.show()
点触验证码
# 识别思路
1. 截取全屏
2. 根据坐标截取验证码区域图片
3. 上传至超级鹰平台获取坐标
4. 模拟点击
Scrapy
爬虫框架
组成
| 组件 | 功能 |
|---|---|
| Engine(引擎) | 负责处理系统的数据流、触发事务,是整个框架的核心 |
| Scheduler(调度器) | 负责处理所有的Request,并按照一定的方式将其进行整理、排列和入队。此外,Scheduler可以自动去除重复的URL,如果特定Request的URL不需要去重也可以通过设置实现,如POST请求 |
| Downloader(下载器) | 负责下载网页内容 |
| Spider | 负责解析所有的Response,提取Item所需的数据和新的URL。用户可以在一个项目中编写多个Spider,每个负责处理一个特定(或一些)网站 |
| Item Pipeline(项目管道) | 负责处理Item,常见的处理有清理、验证和存储数据等 |
| Downloader Middleware(下载中间件) | 位于Engine和Downloader之间,可以自定义扩展下载功能,如自动更换User-Angent、设置代理IP等 |
| Spider Middleware(Spider中间件) | 位于Engine和Spider之间,可以自定义扩展Engine和Spider间通信的功能 |
创建scrapy项目
python
scrapy startproject <name> [path] # 创建scrapy项目
python
<name>/
scrapy.cfg # 项目的配置文件
<name>/
__init__.py # 空文件,将其上级目录变成一个模块
items.py # 项目的目标文件
pipelines.py # 项目的管道文件
settings.py # 项目的设置文件
spiders/ # 项目的Python模块,将会从这里引用代码
__init__.py
定义Item
python
# items.py
# Item对象来实现将爬取到的数据转换成结构化数据的功能,实现方法是定义Item类(继承scrapy.Item类),并定义类中的数据类型为scrapy.Filed字段,该文件已提供一个模板类
class NewsItem(scrapy.Item): #定义NewsItem类
# 定义结构化数据
index = scrapy.Field() #定义排名
title = scrapy.Field() #定义标题
link = scrapy.Field() #定义链接
newsNum = scrapy.Field() #定义搜索结果个数
创建spider脚本
python
scrapy genspider [template] <name> <domain>
# template: 表示创建模板的类型,缺省则使用默认模板
# name: 表示创建的spider脚本名称,创建后会在spiders目录下生成一个以name命名的.py文件
# domain: 表示要爬取网站的域名
# 需要先cd进入spiders目录下执行命令
# ex: scrapy genspider news www.baidu.com
# 生成的spider脚本
import scrapy
class NewsSpider(scrapy.Spider):
name = "news" # 爬虫名称,具备唯一性
allowed_domains = ["www.baidu.com"] # 搜索的域名范围(爬虫的约束区域),规定爬虫只爬取这个域名下的网页,不存 在的URL会被忽略
start_urls = ["https://www.baidu.com"] # 爬取的URL元组/列表。爬虫从这里开始抓取数据
def parse(self, response): # 解析方法,每个初始URL完成下载后将被调用
# 这里写解析代码
pass
运行spider脚本
python
# 在spider目录下运行 添加-o <file>指定保存路径
scrapy crawl <spider_name>