爬虫
网络爬虫(又被称为网页蜘蛛,网络机器人)就是模拟客户端(主要指浏览器)发送网络请求,接收请求响应,一种按照一定的规则,自动地抓取互联网信息的程序。原则上,只要是客户端(浏览器)能做的事情,爬虫都能够做爬虫也只能获取客户端(浏览器)所展示出来的数据
爬虫的作用
1.数据采集
抓取微博评论(机器学习舆情监控)
抓取招聘网站的招聘信息(数据分析、挖掘)
新浪滚动新闻
百度新闻网站
2.软件测试
爬虫之自动化测试
虫师
3 12306抢票
4.网站上的投票
投票网
爬虫的分类
1.根据被爬取网站的数量不同,可以分为:
通用爬虫,如 搜索引擎
聚焦爬虫,如12306抢票,或专门抓取某一个(某一类)网站数据
2 根据是否以获取数据为目的,可以分为:
功能性爬虫,给你喜欢的明星投票、点赞
数据增量爬虫,比如招聘信息
3 根据url地址和对应的页面内容是否改变,数据增量爬虫可以分为:
基于url地址变化、内容也随之变化的数据增量爬虫
url地址不变、内容变化的数据增量爬虫
爬虫的流程
1.获取一个url
2.向url发送请求,并获取响应(需要http协议)
3.如果从响应中提取url,则继续发送请求获取响应
4.如果从响应中提取数据,则将数据进行保存
反爬虫
由于爬虫发起请求的频次可达每秒上百次,会严重增加服务器的负载,而且这些请求都是无效请求,严重影响其他正常用户的浏览。公司可免费查询的资源被批量抓走,丧失竞争力,这样少赚钱。上述均是公司反爬的原因
反爬的三个方向
基于身份识别进行反爬
1.通过headers字段来反爬,headers中有很多字段,这些字段都有可能会被对方服务器拿过来进行判断是否为爬虫
1.1 通过headers中的User-Agent字段来反爬
反爬原理:爬虫默认情况下没有User-Agent,而是使用模块默认设置
解决方法:请求之前添加User-Agent即可;更好的方式是使用User-Agent池来解决(收集一堆User-Agent的方式,或者是随机生成User-Agent)
1.2 通过referer字段或者是其他字段来反爬
反爬原理:爬虫默认情况下不会带上referer字段,服务器端通过判断请求发起的源头,以此判断请求是否合法
解决方法:添加referer字段
1.3 通过cookie来反爬
反爬原因:通过检查cookies来查看发起请求的用户是否具备相应权限,以此来进行反爬
解决方案:进行模拟登陆,成功获取cookies之后在进行数据爬取
2.通过请求参数来反爬请求参数的获取方法有很多,向服务器发送请求,很多时候需要携带请求参数,通常服务器端可以通过检查请求参数是否正确来判断是否为爬虫
2.1 通过从html静态文件中获取请求数据(github登录数据)
反爬原因:通过增加获取请求参数的难度进行反爬
解决方案:仔细分析抓包得到的每一个包,搞清楚请求之间的联系
2.2 通过发送请求获取请求数据
反爬原因:通过增加获取请求参数的难度进行反爬
解决方案:仔细分析抓包得到的每一个包,搞清楚请求之间的联系,搞清楚请求参数的来源
2.3 通过js生成请求参数
反爬原理:js生成了请求参数
解决方法:分析js,观察加密的实现过程,通过js2py获取js的执行结果,或者使用selenium来实现
2.4 通过验证码来反爬
反爬原理:对方服务器通过弹出验证码强制验证用户浏览行为
解决方法:打码平台或者是机器学习的方法识别验证码,其中打码平台廉价易用,更值得推荐
基于爬虫行为进行反爬
1 基于请求频率或总请求数量爬虫的行为与普通用户有着明显的区别,爬虫的请求频率与请求次数要远高于普通用户
1.1 通过请求ip/账号单位时间内总请求数量进行反爬
反爬原理:正常浏览器请求网站,速度不会太快,同一个ip/账号大量请求了对方服务器,有更大的可能性会被识别为爬虫
解决方法:对应的通过购买高质量的ip的方式能够解决问题/购买个多账号
1.2 通过同一ip/账号请求之间的间隔进行反爬
反爬原理:正常人操作浏览器浏览网站,请求之间的时间间隔是随机的,而爬虫前后两个请求之间时间间隔通常比较固定同时时间间隔较短,因此可以用来做反爬
解决方法:请求之间进行随机等待,模拟真实用户操作,在添加时间间隔后,为了能够高速获取数据,尽量使用代理池,如果是账号,则将账号请求之间设置随机休眠
1.3 通过对请求ip/账号每天请求次数设置阈值进行反爬
反爬原理:正常的浏览行为,其一天的请求次数是有限的,通常超过某一个值,服务器就会拒绝响应
解决方法:对应的通过购买高质量的ip的方法/多账号,同时设置请求间随机休眠
2 根据爬取行为进行反爬,通常在爬取步骤上做分析
2.1 通过js实现跳转来反爬
反爬原理:js实现页面跳转,无法在源码中获取下一页url
解决方法: 多次抓包获取条状url,分析规律
2.2 通过蜜罐(陷阱)获取爬虫ip(或者代理ip),进行反爬
反爬原理:在爬虫获取链接进行请求的过程中,爬虫会根据正则,xpath,css等方式进行后续链接的提取,此时服务器端可以设置一个陷阱url,会被提取规则获取,但是正常用户无法获取,这样就能有效的区分爬虫和正常用户
解决方法: 完成爬虫的编写之后,使用代理批量爬取测试/仔细分析响应内容结构,找出页面中存在的陷阱
2.3 通过假数据反爬
反爬原理:向返回的响应中添加假数据污染数据库,通常家属剧不会被正常用户看到
解决方法: 长期运行,核对数据库中数据同实际页面中数据对应情况,如果存在问题/仔细分析响应内容
2.4 阻塞任务队列
反爬原理:通过生成大量垃圾url,从而阻塞任务队列,降低爬虫的实际工作效率
解决方法: 观察运行过程中请求响应状态/仔细分析源码获取垃圾url生成规则,对URL进行过滤
2.5 阻塞网络IO
反爬原理:发送请求获取响应的过程实际上就是下载的过程,在任务队列中混入一个大文件的url,当爬虫在进行该请求时将会占用网络io,如果是有多线程则会占用线程
解决方法: 观察爬虫运行状态/多线程对请求线程计时/发送请求钱
2.6 运维平台综合审计
反爬原理:通过运维平台进行综合管理,通常采用复合型反爬虫策略,多种手段同时使用
解决方法: 仔细观察分析,长期运行测试目标网站,检查数据采集速度,多方面处理
基于数据加密进行反爬
1.对响应中含有的数据进行特殊化处理通常的特殊化处理主要指的就是css数据偏移/自定义字体/数据加密/数据图片/特殊编码格式等
反爬思路: 使用自有字体文件
解决思路:切换到手机版/解析字体文件进行翻译
反爬思路:源码数据不为真正数据,需要通过css位移才能产生真正数据 - 解决思路:计算css的偏移
反爬原理:通过js动态生成
解决思路:解析关键js,获得数据生成流程,模拟生成数据
爬虫的基本使用
import json
import requests
import time
import re
from requests.exceptions import RequestException
def get_one_page(url):
try:
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60"}
response = requests.get(url,headers=headers)
if response.status_code ==200:
return response.text
return None
except RequestException:
return None
def parse_one_page(html):
pat=r'<dd>.*?board-index.*?>(\d+)</i>.*?alt=.*?class=.*?src="(.*?)".*?name"><a.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>'
# 匹配索引,图片网址,电影标题,演员名称,电影日期,评分时匹配了两部分,两部分进行拼接生成的,以group的方式返回
re_movie = re.compile(pat, re.S)
items = re_movie.findall(str(html))
for item in items:
yield {
'index':item[0],
'image':item[1],
'tittle':item[2],
'actor':item[3].strip()[3:],
'time':item[4].strip()[5:],
'score':item[5]+item[6]}
def write_to_file(content):
with open("猫眼电影排行1.txt","a",encoding="utf-8") as f:
f.write(json.dumps(content,ensure_ascii=False) + "\n")
def main(offset):
url = "https://maoyan.com/board/4?offset=" + str(offset)
html = get_one_page(url)
for item in parse_one_page(html):
print(item)
write_to_file(item)
if __name__=="__main__":
for i in range(100):
main(offset=i*10)
time.sleep(2)
scrapy
Scrapy是一个Python编写的开源网络爬虫框架。它是一个被设计用于爬取网络数据、提取结构性数据的框架.
其流程可以描述如下:
爬虫中起始的url构造成request对象-->爬虫中间件-->引擎-->调度器
调度器把request-->引擎-->下载中间件--->下载器
下载器发送请求,获取response响应---->下载中间件---->引擎--->爬虫中间件--->爬虫
爬虫提取url地址,组装成request对象---->爬虫中间件--->引擎--->调度器,重复步骤2
爬虫提取数据--->引擎--->管道处理和保存数据
scrapy使用
pip install scrapy
scrapy startproject testspider # 创建项目
cd testspider
scrapy genspider jd jd.com # 生成爬虫
在items中定义需要爬取的内容
class TestspiderItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
price = scrapy.Field()
sales = scrapy.Field()
title = scrapy.Field()
nick = scrapy.Field()
loc = scrapy.Field()
detail_url = scrapy.Field()
nid = scrapy.Field()
sellerid = scrapy.Field()
在中间件里面定义需要使用的UserAgent和代理IP
京东,淘宝会检测爬虫,需要使用多个IP和UserAgent
class RandomUserAgentMiddleware(object):
def process_request(self, request, spider):
ua = random.choice(USER_AGENT_LIST)
if ua:
request.headers.setdefault('User-Agent', ua)
class ProxyMiddleware(object):
def process_request(self, request, spider):
proxy = random.choice(PROXIES)
request.meta['proxy'] = proxy
定义一个LoginMiddleware使用selenium模拟登陆,返回html,由于现在很多网站有检测selenium的工具,建议使用pyppeteer
https://miyakogi.github.io/pyppeteer/reference.html
模拟用户登陆可以爬取到更多的数据
在pipelines中定义数据保存的格式,是否需要保存至数据库等
class TestspiderPipeline:
def open_spider(self, spider):
client = pymongo.MongoClient(host='127.0.0.1', port=27017)
db = client['jd']
self.q = db[spider.settings['KEY_WORDS']]
self.file = open(KEY_WORDS + '.json', 'w', encoding='utf8')
def process_item(self, item, spider):
content = json.dumps(dict(item), ensure_ascii=False) + '\n'
self.file.write(content)
self.q.update({'nid': item['nid']}, {'$set': dict(item)}, True)
return item
def close_spider(self, spider):
self.file.close()
在settings中将ROBOTSTXT_OBEY改False;取消注释ITEM_PIPELINES,指定数据保存方式,指定代理ip和useragent
在jd.py文件中定义,start_request和parse方法
运行scapy爬虫
scrapy crawl jd
当然爬虫还有比较细的技术点,例如爬虫怎么识别验证码,ip池,cookie具体怎么搭建,JS反爬绕过, app爬虫等。