正则表达式
Xpath
BeautifulSoup
pyquery
Scrapy的selector的用法
Selenium自动测试库学习

1.正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
'.'     默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
'^' 匹配字符串的开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
'$' 匹配字符结尾, 若指定flags MULTILINE ,re.search('foo.$','foo1\nfoo2\n',re.MULTILINE).group() 会匹配到foo1
'*' 匹配*号前的字符0次或多次, re.search('a*','aaaabac') 结果'aaaa'
'+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
'?' 匹配前一个字符1次或0次 ,re.search('b?','alex').group() 匹配b 0次
'{m}' 匹配前一个字符m次 ,re.search('b{3}','alexbbbs').group() 匹配到'bbb'
'{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']
'|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
'(...)' 分组匹配, re.search("(abc){2}a(123|45)", "abcabca456c").group() 结果为'abcabca45'
'[...]' 用来表示一组字符,单独列出,比如匹配[amk]匹配a,m或k
'[^...]' 用来匹配不在[]里面的字符:如[^abc]匹配除了a,b,c之外的字符

'\A' 只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的,相当于re.match('abc',"alexabc") 或^
'\Z' 匹配字符结尾,同$
'\d' 匹配数字0-9
'\D' 匹配非数字
'\w' 匹配[A-Za-z0-9],还包括下划线‘_’
'\W' 匹配非[A-Za-z0-9]
'\s' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t'
'(.*?)' 贪婪匹配,用来获取目标字符
'.*?' 非贪婪匹配,用来替换任意非目标字符

常用方法:

re.match()

re.match()方法会从字符串的起始位置开始匹配正则表达式,如果匹配,就返回匹配成功的结果,一旦开头不匹配,直接返回None,整个匹配失败

re.search()

re.search():扫描整个字符串,然后返回第一个成功匹配的结果

re.findall()

re.findall():把所有匹配到的字符放到以列表中的元素返回,返回的列表中的每一个元素都是元组类型

re.sub()

re.sub(‘a’,’b’,content) ,即在content中,用b替换a的内容

re.compile()

re.compile(正则表达式,修饰符re.S等)方法将正则表达式字符串编译成正则表达式对象,以便在后面的匹配中复用

修饰符

修饰符 描述
re.I 使匹配对大小写不敏感
re.M 多行匹配
re.S 使.匹配包括换行在内的所有字符

2.xpath

1.导入相关库包
from lxml import etree
2.初始化
html = etree.HTML(response.text)

3.文本内容的获取
标签的属性值获取:/@属性名(如:@href,@class…),返回列表格式
文本内容的获取:/text(),返回列表格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from lxml import etree 
text = '''
<html><body><div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div></body></html>
'''
# 初始化
html = etree.HTML(text)

result = html.xpath('//li[2]/a/text()') # second item
response = html.xpath(".//li[2]/a/@href") # link2.html

4.常用规则

表达式 描述
nodename 选取此节点的所有子节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档的节点,而不考虑它们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
路径表达式 描述
bookstore 选取 bookstore 元素的所有子节点
/bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。索引默认从1开始
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

举例说明如何使用xpath:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests
from lxml import etree

# 1.指定url
url = 'https://ishuo.cn/joke'

# 2.发起请求
headers = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
}
response = requests.get(url=url, headers=headers)

# 3.获取页面内容
page_text = response.text

# 4.数据解析
tree = etree.HTML(page_text)
# 获取所有的li标签(已经经过了xpath插件验证)
li_list = tree.xpath('//div[@id="list"]/ul/li')
# 这段话的意思是:选择所有子孙节点是id="list"的div标签,在选择子节点ul标签,再选择子节点li标签
'''
[<Element li at 0x10e32c248>, <Element li at 0x10e30ae88>,...,<Element li at 0x10ef5bfc8>]
# 注意:Element类型的对象可以继续调用xpath函数,对该对象表示的局部内容进行指定内容的解析
'''
fp = open('./duanZi.txt', 'w', encoding='utf-8') # 一次打开多次写入
for li in li_list:
content = li.xpath('./div[@class="content"]/text()')[0] # 调用text()函数取得段子内容
title = li.xpath('./div[@class="info"]/a/text()')[0] # 获得a标签文本内容

# 5.持久化
fp.write(title + ":" + content + "\n\n") # 段子换行分隔
print("一条数据写入成功")

3.pyquery

导入相关库包

from pyquery import PyQuery as pq

初始化工作

doc = pq(html)字符串初始化
doc = pq(url)URL初始化
doc = pq(filename=””)文件初始化
item = doc('li')) 获取li标签的变量为item

css选择器

表达式 说明
id选择器 使用#
class选择器 使用.
标签选择器 直接使用标签,什么都不加
* 选择所有元素
p 选择所有p标签
div,p 选择所有的div和p标签(并集)
div p 选择div下的所有p标签(子集),空格代表嵌套关系

举例说明:

1
2
3
4
5
6
7
8
9
10
from pyquery import PyQuery as pq
# 初始化
doc = pq(html)
#首先选class=“.list”,空格即使选择list里面的标签,再选class=“item-0”,并列active(实际就是一个整体)
li = doc('.list .item-0.active')
#获取所有的兄弟元素
print(li.siblings())

#在向其中筛选
print(li.siblings('.active'))

pyquery获取信息(跟jQuery用法一样)

1.获取指定属性值:变量名.attr(目标属性名) 或者 变量名.attr.属性名
2.获取文本内容:变量名.text()
3.获取html: 变量名.html()
4.获取父节点: 变量名.parent()
5.获取所有的祖先节点: 变量名.parents()
6.获取所有的兄弟元素: 变量名.siblings()
7.过滤选择器:li = doc("li:first-child")#第一个子节点
8.过滤选择器:li = doc("li:gt(2)")#索引值大于2的
9.找寻某一特定标签:变量名.find(标签名).eq(n) n从0开始,表示find某标签,并选择第n个目标值

遍历(重要)

pyquery的遍历需要使用items()方法
我们可以观察到,pyquery的选择结果可能是多个节点,也可能是单个节点,但类型都是PyQuery类型。

  • 对于单节点来说,可以直接打印输出,也可以直接转换为字符串
  • 对于多个节点来说,就必须使用遍历items()函数来获取了(PS:这里的items函数不同于字典的items函数,注意区分

举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pyquery import PyQuery as pq
html = """
.stock_sub .stockTable a
<div class= "stock_sub">
<li class="stockTable">
<a href="link1.html"> link1.html </a>
<a href="link2.html"> link2.html </a>
<a href="link3.html"> link3.html </a>
</li>
</div>
"""

# 初始化
doc = pq(html)
# 查找目标节点
result = doc(".stock_sub .stockTable a") # 单节点可以直接打印
for i in range(len(result)):
print(result.eq(i).attr("href"), result.eq(i).text())

# 多节点测试
lis = doc(".stock_sub .stockTable a") # 这里有多个a标签节点
for item in lis.items():
print(item.attr("href"), item.text())

4.Selenium库学习

动态渲染页面不仅仅涉及到Ajax技术,绝大多数的网页是通过Javascript计算生成新页面的,比如说百度Echarts,淘宝,中国青年网。。。

4.1基本库导入

1
2
3
4
5
6
7
8
9
10
11
12
from selenium import webdrive
from selenium.webdrive.common.by import By
# 若想要模拟键盘,就需要导入keys()类
from selenium.webdrive.common.keys import Keys
# 等待条件类
from selenium.webdrive.support import expected_conditions as EC
# 等待条件
from selenium.webdrive.support.wait import WebDriverWait
# 监听鼠标事件,声明动作链
from selenium.webdrive import ActionChains
# 导入下拉选择框Select类
from selenium.webdriver.support.select import Select

4.2 声明浏览器对象

browser = webdriver.Chrome()/Firefox()/PhantomJS() 等等

4.3 访问目标页面

browser.page_source:打印网页源代码

1
2
3
4
5
6
# 访问目标网址
browser.get("https://www.taobao.com")
# 打印页面源代码
print(browser.page_source)
# 关闭浏览器
browser.close()

4.4 获取单个节点的方法

语法:find_element(By.xx,”XXX”)或者是find_elements(By.xx,”XXX”)
一般常用的是(By.ID,id_name),(By.XPath,xpath_path),(By.CSS_SELECTOR,css_selector_path)

4.5 节点交互

Selenium可以驱动浏览器执行一些列操作,可以模仿一些列简单的人的动作:
nodeName.send_keys(“content”):输入目标节点框文字
nodeName.clear():清空目标节点框文字
nodeName.click():点击目标节点

控制浏览器的操作方法 说明
click() 点击目标节点
set_window_size() 设置浏览器的大小
back() 控制浏览器后退
forward() 控制浏览器前进
refresh() 刷新当前页面
clear() 清除文本
send_keys (value) 模拟按键输入
close() 关闭浏览器
quit() 关闭所有窗口
submit() 用于提交表单
is_displayed() 设置该元素是否用户可见
size 返回元素的尺寸
location 返回元素在浏览器中的位置,以字典的形式返回
get_attribute(name) 获取元素节点的属性值
text 获取元素的文本内容

2019-12-30:新增关于location和size方法的说明
先看示例源代码:

1
2
3
4
5
# 显示等待图片节点加载出来
img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))
time.sleep(2)
location = img.location
size = img.size

这里用了显示等待获取到了我要识别的滑动验证码图片对象,接下来调用该对象的location属性和size属性。
location属性可以返回该图片对象(既这张图片)在浏览器中的位置(坐标轴是以屏幕左上角为原点,x轴向右递增,y轴像下递增),以字典的形式返回

1
2
3
4
5
6
7
8
9
10
{"x":30,
"y":30
}
# 这里我们假设图片的位置是(30,30)

size属性同样返回一个字典,size属性是图片对象的高度,宽度。
{
"height":30,
"width":30
}

4.6 鼠标事件

在WebDriver中,将这些关于鼠标操作的方法封装在 ActionChains 类提供。详情ActionChains解析参考

鼠标监听方法 说明
ActionChains(driver) 构造ActionChains对象
move_to_element(above) 执行鼠标悬停操作
context_click() 右击
double_click() 双击
drag_and_drop() 拖动
move_to_element(above) 执行鼠标悬停操作
context_click() 用于模拟鼠标右键操作, 在调用时需要指定元素定位
click_and_hold(on_element=None) 点击鼠标左键,不松开
drag_and_drop_by_offset(source, xoffset, yoffset) 拖拽到某个坐标然后松开
move_to_element_with_offset(to_element, xoffset, yoffset) 移动到距某个元素(左上角坐标)多少距离的位置
perform() 执行所有 ActionChains 中存储的行为,可以理解成是对整个操作的提交动作

————————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 淘宝滑块的拖动
def login(self, username, password, chromedriverpath, **kwargs):
# 基本设置
browser = webdriver.Chrome(executable_path=chromedriverpath, options=self.chrome_opts)
browser.get(self.login_url)
driver_wait = WebDriverWait(browser, 60)
# 点击'亲, 请登录', 进入登录界面
button = driver_wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'h')))
button.click()
# 输入用户名密码
username_sender = driver_wait.until(EC.presence_of_element_located((By.ID, 'fm-login-id')))
username_sender.send_keys(username)
password_sender = driver_wait.until(EC.presence_of_element_located((By.ID, 'fm-login-password')))
password_sender.send_keys(password)
time.sleep(2)
# 检查是否出现了滑动验证码
try:
slider = browser.find_element_by_xpath("//span[contains(@class, 'btn_slide')]")
if slider.is_displayed():
ActionChains(browser).click_and_hold(on_element=slider).perform()
ActionChains(browser).move_by_offset(xoffset=258, yoffset=0).perform()
ActionChains(browser).pause(0.5).release().perform()
except:
pass
# 点击登录按钮
button = driver_wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'password-login')))
button.click()

4.7 多表单切换,点击详情

    在Web应用中经常会遇到frame/iframe表单嵌套页面的应用,WebDriver只能在一个页面上对元素识别与定位,对于frame/iframe表单内嵌页面上的元素无法直接定位。这时就需要通过switch_to.frame()方法将当前定位的主体切换为frame/iframe表单的内嵌页面中。

鼠标监听方法 说明
switch_to.frame() 将当前定位的主体切换为frame/iframe表单的内嵌页面中
switch_to.default_content() 跳回最外层的页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from selenium import webdriver

driver = webdriver.Chrome()
driver.get("http://www.126.com")
# 切换到id为x-URS-iframe的iframe表单
driver.switch_to.frame('x-URS-iframe')
driver.find_element_by_name("email").clear()
driver.find_element_by_name("email").send_keys("username")
driver.find_element_by_name("password").clear()
driver.find_element_by_name("password").send_keys("password")
driver.find_element_by_id("dologin").click()
driver.switch_to.default_content()

driver.quit()

switch_to.frame() 默认可以直接取表单的id 或name属性。如果iframe没有可用的id和name属性,则可以通过下面的方式进行定位。

先通过xpth定位到iframe
xf = driver.find_element_by_xpath(‘//*[@id=”x-URS-iframe”]’)

再将定位对象传给switch_to.frame()方法
driver.switch_to.frame(xf)

driver.switch_to.parent_frame()

4.8 设置元素等待

元素等待分为显示等待(推荐:显式等待使WebdDriver等待某个条件成立时继续执行,否则在达到最大时长时抛出超时异常(TimeoutException))与隐式等待
selenium.webdriver.support.ui 和selenium.webdriver.support.wait都是用来做显式等待的,但两者没有任何一丢丢的区别,将ui换成了wait,这样更直接易懂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
# 导入时间等待库文件
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("http://www.baidu.com")
# 最长超时时间为10s,检测间隔为0.5s
wait = WebDriverWait(driver,10,0.5)
element = wait.until(
# 等待节点出现的含义
EC.presence_of_element_located((By.ID, "kw"))
)
# 输入文本
element.send_keys('selenium')
time.sleep(10)
# 浏览器退出
driver.quit()

2020-1-11 新增:

4.9 下拉框选择操作

导入选择下拉框Select类,使用该类处理下拉框操作
方法:select_by_value(“选择值”):相当于我们使用鼠标选择下拉框的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from selenium import webdriver
from selenium.webdriver.support.select import Select
from time import sleep

driver = webdriver.Chrome()
driver.get('http://www.baidu.com')

#1.鼠标悬停至“设置”链接
driver.find_element_by_link_text('设置').click()
sleep(1)
#2.打开搜索设置
driver.find_element_by_link_text("搜索设置").click()
sleep(2)

#3.搜索结果显示条数
sel = driver.find_element_by_xpath("//select[@id='nr']")
Select(sel).select_by_value('50') # 显示50条

sleep(3)
driver.quit()

4.10 警告框处理

在WebDriver中处理JavaScript所生成的alert、confirm以及prompt十分简单,具体做法是使用 switch_to.alert 方法定位到 alert/confirm/prompt,然后使用text/accept/dismiss/ send_keys等方法进行操作。

鼠标监听方法 说明
text 返回 alert/confirm/prompt 中的文字信息
accept() 接受现有警告框
dismiss() 解散现有警告框
send_keys(keysToSend) 发送文本至警告框。keysToSend:将文本发送至警告框

2020-3-30:新增

4.11 正确移除Selenium中window.navigator.webdriver的值

前言:
        有不少朋友在开发爬虫的过程中喜欢使用Selenium + Chromedriver,以为这样就能做到不被网站的反爬虫机制发现。

        先不说淘宝这种基于用户行为的反爬虫策略,仅仅是一个普通的小网站,使用一行Javascript代码,就能轻轻松松识别你是否使用了Selenium + Chromedriver模拟浏览器。

        这里我就不详细阐述了,Kingname青南大佬已经给出了解决方案(亲测成功)。

1
2
3
4
5
6
7
8
9
10
11
12
from selenium import webdriver

# 声明一个浏览器
driver = webdriver.Chrome()
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
driver.get('http://exercise.kingname.info')

2020-5-12:新增
      上述的阐述了移除Selenium中window.navigator.webdriver的值的方法:但有些网站,点击了它的页面超链接以后,会自动以新的标签页打开,这种情况下就无法使用上面提到的技巧了(即在webdriver测试控制阶段,若点击跳转链接后,浏览器会新开一个选项页,会重新请求所有的url)

      要解决这个问题实际上非常简单,我们只需要知道一点点HTML知识和 JavaScript 的知识即可。如果要用一个a标签的链接在当前页面打开,我们只需要设置它的target属性值为_self。

      现在我们需要用 JavaScript 把当面页面上的所有a标签的target属性值全部改成_self。

将所有的a标签的target属性设置为_self:

1
2
3
let a_list = document.getElementsByTagName('a');
// 匿名函数,map函数以及“...”表示ES6的扩展运算符
[...a_list].map((a)=> {a.setAttribute('target', '_self')});

使用这个方法有4个地方需要注意:

  • 不适用于通过 JavaScript 的 window.open()函数打开新网址的情况
  • 对于<form>标签的表单提交,也可以设置target=”_self”属性。
  • 必须等页面完全加载完成才能执行这两行 JavaScript 语句。如果执行语句以后,页面通过 Ajax 或者其他途径又加载了新的 HTML,那么需要重新执行。
  • 每次打开新的链接以后,需要再次执行这两行语句。

      这个方法可以与本文开始提到的那篇文章中的方法结合起来使用。首先通过Page.addScriptToEvaluateOnNewDocument让当前标签页的window.navigator.webdriver属性消失,等页面完全加载完成以后,再通过driver.execute_script()运行本文讲到的两行 JavaScript 代码,强迫网页在当前标签页打开新的链接。

4.12 selenium全屏截图

当我们对一些页面进行截图时,发现网页的宽高超出了我们的想象,那这个时候我们应该如何全截屏呢?通过set_window_size属性设置宽高

1
2
3
4
5
6
7
8
9
width = browser.execute_script("return Math.max(document.body.scrollWidth, document.body.offsetWidth, "
"document.documentElement.clientWidth, document.documentElement.scrollWidth, "
"document.documentElement.offsetWidth);")

height = browser.execute_script("return Math.max(document.body.scrollHeight, document.body.offsetHeight, "
"document.documentElement.clientHeight, document.documentElement.scrollHeight, "
"document.documentElement.offsetHeight);")

browser.set_window_size(int(width), int(height))

4.13 selenium添加cookies

selenium的cookie不同于requests的cookie格式,我们可以使用requests库模拟登录后,将cookie存储为json文件,然后通过json.loads读取出来(严格按照这样做)

  • selenium先访问一次想要携带cookie的网址,然后在删除所有由于当前登录所产生的cookies
  • selenium的cookie添加是循环遍历添加的,每一组cookie都有name和value
  • 添加完cookie后再一次访问目标网址
  • 严格按照这样做,否则会报错(经过多次体验)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 载入cookie时效性文件
with open(path, "r", encoding='utf-8') as f:
cookies = json.load(f)

# 尝试请求泛微OA主页,检测当前cookie是否有效

url = "http://IP/WorkflowRequestPictureInner.jsp?workflowid={}".format(workId)
browser = webdriver.Chrome()
# 第一次访问url
browser.get(url)

# 往browser里添加cookies前,先删除所有cookies
browser.delete_all_cookies()
for k, v in dict(cookies).items():
browser.add_cookie({"name": k, "value": v})

# 再一次访问url
browser.get(url)

4.14 selenium的无头模式

无头模式,顾名思义,就是不会新开浏览器界面,而是调用其内核来执行,那如何设置呢?

1
2
3
4
5
6
7
8
# 声明谷歌浏览器对象
driver = webdriver.Chrome()

#设置浏览器无头模式
option = webdriver.ChromeOptions()
option.add_argument('--headless') # 启用无头模式
option.add_argument('--disable-gpu') # 禁止使用GPU
driver = webdriver.Chrome(chrome_options=option)

5.HTTP状态码的理解

状态码304 Not Modified

当我们爬取某些网站时扑飞漫画中会遇到对浏览器缓存下请求资源返回304的情况流量的计费的情况,这里就需要了解HTTP 304的响应状态的资源更新机制。

这是因为该网站采取了强缓存验证, 服务器将要爬取的内容在本地做了缓存,再次请求的时候,会首先检查本地缓存中是否已存在,如果有就返回304

首先看一个关于304请求的响应头的信息,这里面有两个比较重要的请求头字段:If-Modified-SinceIf-None-Match,这两个字段表示发送的是一个条件请求。

当客户端缓存了目标资源但不确定该缓存资源是否是最新版本的时候, 就会发送一个条件请求,这样就可以辨别出一个请求是否是条件请求,在进行条件请求时,客户端会提供给服务器一个If-Modified-Since请求头,其值为服务器上次返回的Last-Modified响应头中的Date日期值,还会提供一个If-None-Match请求头,值为服务器上次返回的ETag响应头的值。

服务器会读取到这两个请求头中的值,判断出客户端缓存的资源是否是最新的,如果是的话,服务器就会返回HTTP/304 Not Modified响应头, 但没有响应体.客户端收到304响应后,就会从本地缓存中读取对应的资源.

解决方案:

  • 方案一:请求头里的 If-Modified-Since、If-None-Match和Cache-control,都必须禁用
  • 方案二:请求头里User-Agent为动态的
1
2
3
4
5
6
7
8
9
10
headers = {
"Host": "www.pufei8.com",
"Referer": "http://www.pufei8.com/",
'Accept': 'text/html',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3945.88 Safari/537.36"
}

参考文章


 评论

联系我 | Contact with me

Copyright © 2019-2020 谁知你知我,我知你知深。此恨经年深,比情度日久

博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议