目录
1. 爬虫、反爬虫与反反爬虫
- 爬虫
自动获取网页信息的程序。
- 反爬虫
阻止爬虫程序获取网页信息的程序。
- 反反爬虫
应对反爬虫程序,爬取网页信息的程序。
其中,爬虫和反反爬虫是用户的行为;反爬虫是服务器的行为。
2.常见的反爬虫技术
主要包括以下四种:
1)Headers校验
2)动态页面
3)IP限制
4)验证码
- Headers校验
HTTP的请求头(headers)是指每次向网络服务器发送请求时传递的一组属性和配置信息。
HTTP定义了十多种请求头类型,但只有几个字段比较常用。对 HTTP 请求头的每个属性进行“是否具有人性”的检查,就是为了阻挡爬虫程序。
请求头可以通过requests模块进行自定义。
实例:
下面程序可用于显示浏览器访问请求头信息。https://www.whatismy browser.com/ 可以获取本机配置信息。
import requests
from bs4 import BeautifulSoup
session = requests.Session()
headers = {"User-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
"Accept": "text/html, application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"}
url = "https://www.whatismybrowser.com/developers/what-http-headers-is-my-browser-sending"
req = session.get(url, headers=headers)
bsObj = BeautifulSoup(req.text, features="lxml")
print(bsObj.find("table", {"class": "table-striped"}).get_text)
通常headers中最重要的参数之一是 User-Agent。无论做什么项目,一定要记得把 User-Agent 属性设置成不容易引起怀疑的内容,不要用Python-urllib/3.4之类的。(浏览器的User-Agent属性值,可以在Chrome浏览器中任意打开一个网页,右键点击检查,在Network选项卡中,任意打开一个请求,就可以得到这个属性值,以后在程序中使用时,直接copy。)
另外,如果你正在处理一个警觉性非常高的网站,就要注意那些经常用却很少检查的请求头,比如 Accept-Language 属性,也许它正是那个网站判断你是否为人类访问者的关键。(Headers中的各种属性值,都可以在上图中找到。)
1)User-Agent
User Agent,中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
在网络请求当中,User-Agent 是标明身份的一种标识。网站通过判断 UA 来给不同的操作系统、不同的浏览器发送不同的页面,因此可能造成某些页面无法在某个浏览器中正常显示,但通过伪装UA 可以绕过检测。
UA反爬虫:是一种黑名单策略,只要出现在黑名单中的请求,都视为爬虫,对于此类请求将不予处理或返回错误提示。
绕过 UA反爬虫:将爬虫程序中请求头的 User-Agent 的值改为浏览器的User-Agent ,这样就能够欺骗服务器,达到绕过反爬虫的目的。
2)referer
Referer是HTTP协议中的一个请求报头,用于告知服务器,用户的来源页面。如果从一个网页跳转到另一个网页,http 请求头里会带有Referer参数。
Referer主要用于统计,像CNZZ、百度统计等可以通过Referer统计访问流量的来源和搜索的关键词(包含在URL中)等等,方便站长们有针性对的进行推广和SEO。另一个用处是防盗链。
盗链:是指服务提供商自己不提供服务的内容,通过技术手段绕过其它有利益的最终用户界面(如广告),直接在自己的网站上向最终用户提供其它服务商的服务内容,骗取最终用户的浏览和点击率。受益者不提供资源或者提供很少的资源,而真正的服务提供商却得不到任何的利益。
Referer防盗链:服务器通过检测 Referer 是否来自指定域名,从其他非服务提供者指定的HTTP请求得不到正常结果。主要用于图片、视频和网盘服务器。
绕过referer防盗链:修改请求头中 Referer值。
实例:
下载天涯社区某页面的一张图片:http://bbs.tianya.cn/post-ehomephot-50239-1.shtml
import requests
url = 'http://img3.laibafile.cn/p/m/308757865.jpg' #图片的地址
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36',
'Referer': 'http://bbs.tianya.cn/post-ehomephot-50239-1.shtml'} #Referer指明我们是来自天涯社区,如果不加的话 将无法爬取该图片
response = requests.get(url, headers=headers)
print(response.status_code)
with open('referer_photo.jpg', 'wb') as f:
f.write(response.content)
完整代码:
import requests
from requests import RequestException
def get_response(url):
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36',
'Referer': 'http://bbs.tianya.cn/post-ehomephot-50239-1.shtml'
}
response = requests.get(url, headers=headers)
print(response.status_code)
if response.status_code == 200:
response.encoding = response.apparent_encoding
return response
return None
except RequestException:
print('RequestException')
return None
url = 'http://img3.laibafile.cn/p/m/308757865.jpg'
response = get_response(url)
with open('1.jpg', 'wb') as f:
f.write(response.content)
不在Headers中加Referer属性,将无法爬取该图片,结果如下:
加了Referer属性后,表明我们是来自天涯社区,此时则可以爬取,结果如下:
(3) cookies
Cookies:指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据。简单来说Cookie就是一个浏览器上的缓存,前端工程师有时用它当数据库使。
Cookies反爬虫:常见的Cookie反爬有两种,一种是在后端设置,通过Response的Header传输到前端浏览器中;另一种则通过前端调用document.cookie来设置。对于反爬效果来说,第二种比第一种效果好。
绕过cookies反爬虫:第一种通过修改请求头中 cookie 值;第二种则通过动态页面技术。
- IP限制
IP限制:服务器会检测某个IP 在单位时间内的请求次数,如果超过了某个阈值,那么服务器会直接拒绝服务,返回错误信息。这种情况称为封IP。
绕过IP 限制反爬虫:借助代理方式来伪装IP,让服务器无法识别由本机发起的请求,这样就可以成功防止封IP。
- 动态页面
有时在用requests抓取页面的时候,在浏览器中可以看到正常显示的页面数据,但是使用requests得到的结果并没有显示的内容。这是因为requests获取的都是原始的HTML文档,而浏览器中的页面则是经过JavaScript 处理数据后生成的结果。
这些数据的来源有多种,可能是通过Ajax加载的,可能是包含在HTML文档中的,也可能是经过JavaScript和特定算法计算后生成的。
Ajax:
Ajax---Asynchronous JavaScript and XML,异步的JavaScript和XML。它不是一门编程语言,而是利用JavaScript在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页的技术。
Ajax加载方式,可以直接分析Ajax请求,仍然借助requests或urllib来实现数据爬取。
JavaScript:
问题:部分页面由Javascript生成,并不包含Ajax请求;难以找出Ajax请求的规律。
解决思路:模拟浏览器运行,抓取渲染之后的页面代码。
Python实现:python提供了模拟浏览器运行的库,如Selenium、Splash等。
- 验证码
验证码(CAPTCHA),“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试),是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试。常用于以下情况:
•登录验证码
•当某一用户访问次数过多后,就自动让请求跳转到一个验证码页面,只有在输入正确的验证码之后才能继续访问网站
常见的验证码主要包括:图片验证码,滑动验证码,点触验证码,宫格验证码。
3.Selenuim库
Selenium是一个强大的网络数据采集工具,因为它们可以直接运行在浏览器上,最初是为网站自动化测试而开发的。近几年,它被广泛用于获取精确的网站快照。Selenium可以让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前页面的源代码,做到可见即可爬。
Selenium 自己不带浏览器,它需要与第三方浏览器结合在一起使用。Selenium支持大部分浏览器,如Chrome、Firefox等,还有Android等手机端的浏览器,以及无界面浏览器PhantomJS。
前提:
需要安装Chrome浏览器,以及与Chrome浏览器版本相对应的ChromeDriver。具体细节可以自行百度,这里不再赘述。
然后安装Selenium库:pip install selenium
在程序中输入以下代码:
from selenium import webdriver
browser = webdriver.Chrome()
如果弹出下面的界面,说明配置成功:
- selenium库的基本操作
1)访问页面
2) 查找节点
3) 获取节点信息
4)节点交互
5)动作链
6)alert
7)执行JavaScript
8)cookies操作
9)延时等待
10)异常处理
访问页面:
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('http://taobao.com') #访问淘宝
print(browser.page_source) #打印页面源码
此时会弹出淘宝的首页,并且控制台打印出页面源码:
查找节点:
查找单个节点:
find_element_by_name()
find_element_by_id()
find_element_by_css_selector()
find_element_by_xpath()
find_element()
例如查找淘宝首页的搜索输入框:右键点击检查,查看网页源码,点击下图所示的按钮,并将鼠标移动到输入框上,就会自动的帮我们快速定位输入框对应的HTML代码的位置:
input1 = browser.find_element_by_name('q') #根据name属性查找 属性值为q
print(input1)
input2 = browser.find_element_by_id('q') #根据id属性查找 属性值为q
print(input2)
input3 = browser.find_element_by_css_selector('.search-combobox-input') #根据css选择器查找 使用class属性
print(input3)
查找多个节点:
find_elements()
例如查找淘宝首页左侧的所有选项卡:
发现每一行的选项都在li标签下,每个选项对应li标签下的一个a标签:
#只需要找到所有的li标签下的所有的a标签即可
list1 = browser.find_elements_by_xpath('//li[@class="J_Cat a-all"]/a') #多个节点要加s elements 使用xpath解析
print(list1)
获取节点信息:
获取属性:
WebElement.get_attribute()
获取文本值:
WebElement.text
获取id、位置、标签名、大小:
WebElement.id、WebElement.location、
WebElement.tag_name、WebElement.size
例如查看之前查找的所有节点的文本值和href属性值:
for i in list1:
print(i.text)
print(i.get_attribute('href'))
节点交互:
输入文本:
send_keys()
清空文本:
clear()
点击按钮:
click()
前进:
forward()
后退:
back()
例如在之前查找的输入框内输入iphone,并点击搜索按钮进行搜索:
input1.send_keys('iphone')
button = browser.find_element_by_css_selector('.btn-search') #找到搜索按钮
button.click()
动作链:
动作链---没有指定的执行对象,比如鼠标拖曳。
from selenium.webdriver import ActionChains
browser = webdriver.Chrome()
browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
browser.switch_to.frame('iframeResult') # 切换Frame 进入子页面
source = browser.find_element_by_css_selector('#draggable') #找到源位置
target = browser.find_element_by_css_selector('#droppable') #找到目标位置
actions = ActionChains(browser)
actions.drag_and_drop(source, target) #将源位置拖拽到目标位置
actions.perform() #执行
alert:
定位弹出对话框:
switch_to.alert()
获取对话框文本值:
text
相当于点击“确认”:
accept()
相当于点击“取消”:
dismiss()
完成上述操作后,可以关闭浏览器,不过有一个提示框,要先处理提示框,才能关闭浏览器:
browser = webdriver.Chrome()
browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
browser.switch_to.frame('iframeResult') # 切换Frame 进入子页面
source = browser.find_element_by_css_selector('#draggable') #找到源位置
target = browser.find_element_by_css_selector('#droppable') #找到目标位置
actions = ActionChains(browser)
actions.drag_and_drop(source, target) #将源位置拖拽到目标位置
actions.perform() #执行
alert = browser.switch_to.alert
print(alert.text)
alert.accept()
browser.close()
执行Javascript:
对于某些操作,Selenium API没有提供,如下拉进度条,可以模拟运行JavaScript实现.
execute_script()
执行下拉进度条:
browser = webdriver.Chrome()
browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
#执行下拉进度条
browser.execute_script("window.scrollTo(0,document.body.scrollHeight)")
选项卡管理:
browser.execute_script('window.open()')
print(browser.window_handles)
browser.switch_to.window(browser.window_handles[1]) #打开一个新页面
browser.get('https://www.taobao.com') #访问淘宝
browser.get('https://www.baidu.com') #访问百度
browser.back() #返回淘宝
browser.forward() #回到百度
cookies操作:
获取cookies:
get_cookies()
添加cookies:
add_cookie()
删除cookies:
delete_all_cookies()
cookies是一个字典,有很多键值对,下面是一些字段的含义:
browser = webdriver.Chrome()
browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
#获取页面cookies
print(browser.get_cookies())
#添加cookies
browser.add_cookie({'name':'name','value':'value','domain':'.runoob.com'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())
延时等待:
隐式等待:implicitly_wait()
在创建driver时,为浏览器对象创建一个最长等待时间,这个方法是得不到某个元素就等待,直到拿到元素位置(如果一直拿不到就等到时间截止),再执行下一步。
browser = webdriver.Chrome()
browser.implicitly_wait(10) #隐式等待 10s
browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
#browser.switch_to.frame('iframeResult') 如果不切换frame 找不到draggable这个节点 会等待10s后 爆出异常
draggable = browser.find_element_by_id('draggable')
显式等待:WebDriverWait()
明确的要等到某个元素的出现或者是某个元素的可点击等条件,等不到,就一直等,如果在规定的时间之内都没等到则跳出Exception。
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
browser = webdriver.Chrome()
browser.implicitly_wait(10) #隐式等待 10s
browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
browser.switch_to.frame('iframeResult') #如果不切换frame 找不到draggable这个节点 会等待10s后 爆出异常
#定义显式等待
wait = WebDriverWait(browser, 10)
draggable = wait.until(EC.presence_of_element_located((By.ID, 'draggable')))
print(draggable)
browser.close()
显式等待条件:
异常处理:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException,TimeoutException
browser = webdriver.Chrome()
browser.implicitly_wait(10) #隐式等待 10s
try:
browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
except TimeoutException:
print('time out')
browser.switch_to.frame('iframeResult') #如果不切换frame 找不到draggable这个节点 会等待10s后 爆出异常
#定义显式等待
wait = WebDriverWait(browser, 10)
try:
draggable = wait.until(EC.presence_of_element_located((By.ID, 'draggable')))
print(draggable)
except NoSuchElementException:
print("No")
browser.close()
4.实战
- 爬取新浪新闻
爬取新浪新闻中滚动页面的新闻内容,包括题目、时间、来源、正文,并存入MongoDB数据库。
- 爬取人民网新闻
爬取人民网新闻滚动新闻页面的新闻内容,包括题目、时间、来源、正文,并存入MongoDB数据库。