实现思路:
需求分析,首先确定目标数据
分析网站中对应的目标数据如何获取
对部分数据进行抓包分析并确定抓取方案
(先抓包分析登录如何请求以及如何提交数据,分析验证码如何解决,然后创建项目并实现登录与Cookie处理。 )
分析如何实现自动翻页
爬虫项目创建和建立数据库
将分析结果转化为代码(items,爬虫,settings)
爬取数据并写入数据库pipelines,
项目的运行
爬虫整理:就像搭积木,每块用的熟了,用面向对象的思路
面试小技巧:
笔试,不要空题目
面试突出自学能力,自我不满足,体现研究能力(研究源码,前端,后端,项目)
!!!!!!!!思维导读 个人技能,熟悉项目 引导面试官
学习更多,接触更多用户和并发量
基础,flask或drf源码
flask和django相同点:wsgi,路由,视图,模板
不同点:小而精,大而全(细说组件)
同事间不要问技术的问题,业务的问题可以问
小错误需小心:
通用rules和正则url中的?或/ 需要在前面加上转译符\
变量名不要重复
编码问题,先转bytes,再解码
res=requests.get(url)
data=bytes(res.text,res.encoding).decode(\'gbk\',\'ignore\')
正则表达式--验证手机号码:13[0-9]{9}
邮箱验证:^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$
身份证正则合并:(^\d{15}$)|(^\d{17}([0-9]|X)$)
项目:
项目架构图
项目背景
项目人员分配
用过到了哪些技术,知识点
项目的重点难点两点,以及我的解决方法
项目总结:通过这个项目我换有哪些提示
爬虫(文字模板)
目标站点需求分析
涉及的库
获取单页源码
解析单页源码
保存到文件中
开启高性能获取多个页面抓取
为何要爬虫
一般企业需要分析市场 国内市场的需求分析 分析竞争对手
传统调研是到现场 到实体去考察 或者收集
比如你加入了一个国外贸易企业 做欧洲的生意
信息咨询行业 3-5年 每天达到量1000万条的数据
分布式爬取 分布式部署 各种各样的数据库
整体爬虫内容:bj
第一章爬虫介绍
爬虫的分类:聚焦,通用,增量式
robots协议
反爬机制:
反反爬策略
第二章http和https协议
协议概念;client和server进行数据交互的形式
常用请求头信息:User_Agent , Connection
常用响应头信息:Content_Type
三种加密方式:
第三章 requests模块
请求数据方式(两个方法+四种方式)
UA伪装(反反爬策略)
爬取基于ajax请求数据的流程
使用抓包工具获取请求的url
对url发请求,获取页面数据
持久化存储
爬取图片数据的方式
IO
urllib
实现模拟登陆 -
处理cookie(反反爬策略)
手动处理
自动处理
设置代理(反反爬策略)
proxies字典 {\'http\':\'ip+port\'}
使用线程池(multiprocessing.dummy.Pool)实现数据爬取
map(func,list)
第四章 数据解析
正则解析
xpath解析
xpath插件
数据加密-煎蛋网(反反爬策略)
爬取全国所有城市名称(xpath表达式的特殊使用)
常见错误处理:HTTPConnectionPool(host:XX)Max retries exceeded with url
bs4解析
soup.select("a .xxx div")
第五章 验证码处理
第六章 动态数据加载
- selenium
- phantomjs
第七章 移动端数据爬取
- localhost:8888
第八章 scrapy框架基础+持久化存储
第九章 递归解析和post请求
第十章 日志等级和请求传参
第十一章 UA池和代理池
第十二章 scrapy中selenium的应用
第十三章 全站数据爬取
第十四章 分布式爬虫
1.导包 from scrapy_redis.spiders import RedisCrawlSpider
2.修改爬虫文件: 父类,allow_demains和start_urls删除,redis_key
3.爬取数据的程序
4.在配置文件中使用scrapy_redis模块中的管道和调度器
5.在配置文件中进行数据库的ip,port的配置
6.redis数据库中进行配置
7.开启redis
8.执行爬虫文件 scrapy runspider xxx.py
9.给调度器中仍入一个url: lpush xxx www.xxx.com
第十五章 增量式爬虫
回答总原则:根据实际经验回答,思路一定要清晰,一定要把自己的项目经验想办法融入回答中表现自己。
1、爬虫抓取方案有哪些?
从技术的角度来说,……
从策略的角度来说,聚焦与通用,……
若考虑性能问题,……
总之,……
5、你擅长哪些爬虫技术(框架)?
Urllib、requests、Scrapy、pyspider等,比如,我之前…项目使用…做的,……
6、熟悉Linux吗?
还算熟悉,至少能够部署脚本,比如之前做了分布式爬虫,爬……,就是在Linux上做的。
7、如何登录站点?要注意什么?
先分析如何请求传递的数据。要注意Cookie处理、验证码处理等等…比如我之前登录过12306、豆瓣……等,……
8、分布式爬虫怎么架构?
分布式爬虫的架构难点不在于爬虫本身,而在于节点之间的通信与数据去重。
我采用过……和……的架构方案开发过爬虫项目,……
9、有什么方式提高爬虫性能?
很多,比如任务合理分割、多线程、多进程、分布式等……
……
10、怎么解决IP限制?
构建代理IP池,而可靠的IP很重要。
比如可以使用接口获取IP,当然需求量大可以自己构建服务器获取稳定可靠的IP,比如我之前……
效率:
如何提升爬虫的效率
requests方便可以用线程池来提高爬虫代码效率,协程,使用队列减少数据库访问
scrapy配置文件相关配置
增加并发,默认32个,可改为CONCURRENT_REQUESTS=100
日志等级,会有大量日志输出,为了减少cou使用率 LOG_LEVEL=INFO
禁用cookie,如果不是真的需要cookie,可以禁掉 COOKIES_ENADLED=False
禁止重试,失败的http请求会减慢爬虫速度,RETRY_ENABLED=False
减小下载超时,对一个非常慢的链接爬取,会影响效率 DOWNLOAD_TIME=10,
去重:
数据抽取,清洗,去重:
数据抽取清洗列入过滤掉控制,null值,或者改变数据结构,主要用的时pandas进行数据清洗抽取
空值检测删除空值所在的行数据:
isnull notnull any all
原理:
1.将一组boolean值作为df的行索引(True保留)
2.将空所在的行索引获取,使用drop(labels,axis=0)进行删除
3.dropna(axis=0)
空值检测填充空值:
df.fillna(method=\'ffill\',axis=1)
缺失值处理
可通过describe和len直接发现,以及通过0数据发现
处理方式:删除,插补,不处理
插补的方式主要有:体量不大的话,不建议删除.可均值插补,中位数插补,固定值插补(众数插补,最近数据插补,回归插补,拉格朗日插值,牛顿插值法,分段插值等等)
异常值处理:
可通过散点图发现
处理方式:视为缺失值,删除,修补(平均数,中位数等),不处理
重复行检测和删除:
drop_duplicated(keep=‘first/last’)
数据去重:
mysql标志位去重
redis去重
scrapy去重----在调度器里,先导入scrapy.core里的scheduler,点进去看到这个类,其中有个enqueue_request的方法就是执行了去重这个类的request_seen方法,这里面这行了两个函数: 一个是,执行了request.py里request_fingerprint这个方法,里面用了hashlib.sha1这个算法,把每次的request的长度固定(上千万的url在内存中可以应付), 另外一个是,接着在去重里执行了set去重().(上千万去重没问题)
set是用了bitmap方式,将访问过的url通过hash函数映射到某一位
过滤器去重,布隆过滤器性能较高bloom filter,先将BitVector.py+bloom.py放到python3-Lib下.bloomfilter对bitmap进行改进,多重hash函数降低冲突,在bit array是进行标记
from bloom import *
class ListenWeb():
def __init__(self,...):
self.bf1=BloomFilter(0.001,1000000)
self.bf2=BloomFilter(0.001,1000000)
def filter(self,url):
if (self.bf2.is_element_exist(url)):
return 0
如何监控爬虫性能?
自己写脚本,爬虫运行、停止运行(心跳)、崩溃时,主动通知到中心节点,比如我之前写了一个……
通过一些成熟的框架实现性能监控,比如Scrapyd,比如我之前通过……
4、如何解析数据?你喜欢用什么?
有很多工具可以实现,比如正则、XPath、Json、BS…
我喜欢用…,因为……
遇到过得反爬虫策略以及解决方法
这同样是很深的一个话题,就像攻击武器与防御武器一样,双方总是在不断升级。常见的反爬措施(我遇到过的)有下面几种:
1.通过Header封杀
一般浏览器访问网站会有header,比如Safari或者Chrome
通过header,常见UA以及来源网址等,……自定义切换headers,UA pool
2.登录限制 通过cookie,可以用session.request解决
公开信息的网站一般不会有这个限制,这样让用户也麻烦了。其实反爬措施都或多或少的影响真实用户,反爬越严格,误杀用户的可能性也越高。对爬虫来说,登录同样可以通过模拟登录的方式解决,加个cookie就行了(话又说回来,网络的原理很重要)。
基于cookie的登录原理
登录成功之后浏览器会返回给客户端一个cookie,这个cookie可以保存用户的登录状态以及表示当前用户的身份标识,
同时针对cookie可以用session来解决,由于少数的网页分页之间的跳转cookie会发生改变,
针对这类网页的爬取,可以预先走通每个页面来获取页面的cookie,保存到列表中,爬取的时候做选择
登录总结:
两种套路:
- post登录获取cookie,以后携带cookie
- get获取未授权cookie,post登录携带cookie去授权,以后携带cookie
请求头:
user-agent
referer
host
cookie
特殊请求头,查看上一次请求获取内容。
\'X-Anit-Forge-Code\':...
\'X-Anit-Forge-Token\':...
请求体:
- 原始数据
- 原始数据 + token
- 密文
- 找算法
- 使用密文
4.动态网页反爬虫(通过ajax请求数据,或者通过JavaScript生成),
ajax 渲染的发post,找参数
对部分数据进行加密处理的(数据是乱码),需要反推加密的算法
可以使用selenium进行截图,使用python自带的pytesseract库进行识别,但是比较慢,最直接的方法是找到加密的方法进行逆向推理。
抓包工具,断点调试,找到位置,如果参数加密的话,可以在抓包里修改文件上传,增加alert
JS渲染,通过资源随机化存储,……腾讯动漫 url中有一段随机码,可以使用selenium + phantomjs
图片懒加载 没有访问前web不加载,放在src,浏览时,图片scr在真实的scr2身上
5.验证码……对接打码平台
这几乎是终极武器了,验证码是专门用来区分人和计算机的手段。对于反爬方来说,这种方式对真实用户和搜索引擎(其实可以通过记录搜索引擎爬虫的ip来区别对待,可以解决)的危害比较大,相信读者都有输入验证码的痛苦经历。但这种方法也并不是无敌的!通过现在很火的机器学习可以轻松的识别大部分的验证码!Google的reCAPTCHA是一种非常高级的验证码,但是听过通过模拟浏览器也是可以破解的。
1.基于用户行为的爬虫:(同一IP短时间内访问的频率,)切换IP
可以尽量让爬虫想人类一样访问网页(比如随机sleep一段时间,如果每隔3s访问一次网站很显然不是正常人的行为)。
6.ip限制
网站可能将识别的ip永久封杀,这种方式需要的人力比较大,而且误伤用户的代价也很高。但是破解办法却非常简单。目前代理池几乎是搞爬虫的标配了,甚至还有很多高匿代理等好用的东西。所以这基本上只能杀杀小爬虫。
策略:
固定代理ip
ip代理池:4个模块:存储模块、获取模块、检测模块、接口模块。
买ip数量
adsl拨号
7.网站内容反爬
有一些网站将网站内容用只有人类可以接收的形式来呈现(其实反爬就是区别对待人类和机器嘛)。比如将内容用图片的形式显示。但是近几年来人类和机器的差别越来越小,图片可以用OCR准确率非常高地去识别。
常用请求库,解析库,模拟自动化,scrapy框架,分布式
requests模块
爬取数据流程:
get,post作用:
get,post参数:
url
headers
data/params
proxies
处理ajax动态加载的数据:
抓包工具
获取数据包中对应的url
response.json()
模拟登陆: -
打码平台使用:
开发者用户:
创建一个软件(id,key)
下载实例代码(HttpPython)
cookie的处理:
session:requests.Session()
代理ip:
线程池: from mulprocessing.dummy import POOl map(func,list)
图片懒加载:
伪属性
aiohttp 协程模块,一条线程实现
数据解析
正则表达式:
bs4:
实例化解析对象(将页面源码数据加载到该对象中)
通过对象中的相关方法实现页面中标签的定位
取文本 取属性
find(\'element\',attr=\'xxx\'):返回的是单数
find_all()
soup.element
select(选择器):
层级选择器:
分隔符:>,单个层级 空格,多个层级
取文本:element.string .text .get_text()
xpath:
xpath(\'xpath表达式\')
/ or //
属性定位: //div[@class=\'xxx\']/a//span
索引定位://div[1]
模糊定位://div[contains(\'class_\',\'xxx\')] //div[start_with(\'class_\',\'xxx\')]
/text() //text()
//div/@class
xpath插件
局部页面内容的解析: 1.定位到一批li标签 2.单独对li标签表示的页面数据进行数据解析 li.xpath(\'.//\')
selenium
作用:完成浏览器自动化的操作(通过编写代码制定一些列的行为动作,将该行为动作同步映射到浏览器中)
使用流程: 1.from selenium import webdriver 2.实例化某一款浏览器对象(浏览器的驱动程序路径作为参数) 3.制定行为动作
- bro.get(url=url)
- bro.excute_script(‘js’)
- page_source:当前页面源码数据(动态加载出来的数据)
find系列函数:
switch_to.frame(id)函数:
phantomJs:
谷歌无头浏览器:
scrapy
项目创建流程:
scrapy startproject xxxPro
cd xxxPro
scrapy genspider xxx www.xxx.com response.xpath(\'exp\'):返回的列表(Selector) extract/extract_first():
持久化存储:
基于终端指令:scrapy crawl xxx -o xxx.csv
只可以将parse方法的返回值进行持久化存储
基于管道: 1.获得解析到的数据 2.将解析到的数据封装到item对象中 3.将item提交到管道
4.管道类中的process_item方法中的item参数进行item对象的接收 5.开启管道
ITEM_PIULELINE = {
qiubaiPro.pipelines.qiubaiLine:301,
qiubaiPro.pipelines.qiubaiLine110:302
}
open_spider(self,spider)
close_spider(self,spider)
process_item方法中的返回值:
处理分页数据爬取:
手动请求发送:yield scrapy.Request(url,callback) yield scrapy.FormRequest(url,formdata,callback)
post请求
cookie处理:
不需要手动处理cookie
日志等级: LOG_LEVEL = \'ERROR\' LOG_FILE = \'./log.txt\'
请求传参:(传递的是什么呢?答案:item)
场景:在使用scrapy进行数据爬取时,如果解析的数据不在同一张页面中。
scrapy.Requests(url,callback,meta={\'item\':item})
如何接收item:item = response.meta[\'item\']
五大核心组件原理: spider 引擎 调度器 管道 下载器
下载中间件:
process_request(self,request,spider): request.headers[\'User-Agent\'] = \'xxxx\' request.meta[\'proxy\'] = \'http://ip:port\'
from scrapy.http import HtmlResponse
process_response(self,request,response,spider): return HtmlResponse(url,body,request,encoding)
UA池和代理池:
selenium在scrapy中的应用:
spider的init方法中声明一个浏览器对象的属性(浏览器对象必须要实例化一次)
spider的closed(self,spider)方法中关闭浏览器对象
在中间件的process_response方法中获取浏览器对象(spider.bro)
在process_response方法中,获取页面源码数据(bro.page_source(包含了动态加载的数据))
将源码数据赋值给HtmlResponse(url,body,request,encoding)的body参数
crawlSpider:
scrapy genspider -t crawl xxx www.xxx.com
连接提取器:LinkExtactor(allow=\'正则\')
规则解析器:Rule(link,callback,follow=True)
scrapy 集合各种功能:(高性能异步下载,队列,分布式,解析,持久化)
scrapy核心组件工作流程,原理:
scrapy分为五大组件,分别是引擎,调度器,管道,爬虫,下载器
核心是引擎和调度器
engine引擎负责其他四个的通讯和数据传递(处理事件流和触发事件)
schedule调度器负责接收引擎发送过来的requests请求,并按照一定方式进行整理排列,入队,当引擎需要时,交还给引擎, 并提供了过滤重复Request的功能
downloader下载器负责下载 engine引擎发送的所有的requests请求,并将其获取到的response交还给引擎,再次进去调度器
spider爬虫负责处理所有的response,从中获取数据,获取item字段需要的数据,并将其要跟进的url提高给引擎,再次进去调度器
item Pipileine 管道赋值处理spider中获取到的item,并进行后期处理过滤 存储等等
Scrapy的去重原理
找到Request类,需要将dont_filter设置为False开启去重,默认为True,没有开启去重
scrapy的暂停和重启
scrapy的爬虫在运行时,需要暂时停止运行,并在下一次从暂停的地方继续爬取的方法:
1.打开cmd进入虚拟环境,cd到scrapy的main.py目录下;
2.在cmd下输入以下命令
scrapy crawl 爬虫名称 -s JOBDIR=保存进程的文件夹目录
比如我要运行的spider的name为zhihu,文件夹目录是scrapy目录下的job_info/001(001表示这是一次爬取任务,重新新建任务需要重新建立目录,比如002),我需要运行的指令为:
scrapy crawl jobbole -s JOBDIR=job_info/001
3.运行过程中按下Ctrl+c暂停任务,等待处理完未完成的进程(按下两次Ctrl+c为强制结束进程);
4.结束后显示:
[scrapy.core.engine] INFO: Spider closed (shutdown)
5.下次需要重启的时候,输入与第二步相同的命令。
scrapy 的telent 远程协议,可以查看scrapy的setting的值
scrapy的数据收集,statsCollect 收集了比方说request和返回item的次数和运行时间,404数据等
scrapy的信号,开启,停止,抛出异常,空闲时
scrapy的拓展开发,在setting中设置extensions
scrapy.FormRequest(formdata) 发post请求
分布式
scrapy为何不能实现分布式:
scrapy-redis的作用:
实现分布式的方式:
流程:
增量式爬虫
增量式爬虫:每次爬取网站中更新的数据.
定时爬虫:使用脚本在指定时间内进行一次数据(最新更新)的爬取.
爬取数据的流程:
1.指定url
2.根据指定的url发请求,获取页面数据
3.持久化存储
案例:
实现增量式爬虫的方案:
1.在发送请求之前,判断url之前是否爬取过
a.将即将进行爬取的数据对应的url存储到redis的set中.
2.根据爬取到的数据进行重复过滤,然后在进行持久化存储
b.将爬取到的数据给其生成一个唯一的标识(可以将该标识作为mysql的列.可以将该标识存储到redis的set中)
HttpCOnnectionPool:
- 代理ip
- Conection : ‘close’
xpath(xxx | xxx | xxx)
分布式的原理:
scrapy本身不支持分布式,需要依靠scrapy_redis来实现分布式,通过基础RedisSpider来使用scrapy_redis
封装好的调度器和管道实现分布式数据爬取
在配置文件中使用scrapy_redis组件的去重队列以及调度器
scrapy-redis 中爬虫的类继承的时redis.spider,封装了redismixin,里面有个set redis方法,其中每个spider 都有redis_key(redis中列的名字).
和scrapy一样都是继承了baseDupefilter去重,from_setting方法中初始化连接有了server,以及request_seen方法下执行了sadd,判断added是否等于0,0就是重复了
scrapy-redis有三种队列
增量式爬虫
增量式爬虫:每次爬取网站中更新的数据.
定时爬虫:使用脚本在指定时间内进行一次数据(最新更新)的爬取.
爬取数据的流程:
1.指定url
2.根据指定的url发请求,获取页面数据
3.持久化存储
案例:
实现增量式爬虫的方案:
1.在发送请求之前,判断url之前是否爬取过
a.将即将进行爬取的数据对应的url存储到redis的set中.
2.根据爬取到的数据进行重复过滤,然后在进行持久化存储
b.将爬取到的数据给其生成一个唯一的标识--数据指纹hash值(可以将该标识作为mysql的列.可以将该标识存储到redis的set中)
发送请求时判断这个url是不是已经爬取过
在解析内容后判断这部分内容是不是已爬取过
写入存储介质时后判断这部分内容是不是已爬取过
whoosh是轻量级的,是python写的,
jieba分词:重点是分词
mongodb保存文件
http://blog.sina.com.cn/s/blog_13bb711fd0102xjrd.html
https://blog.csdn.net/qq_38584374/article/details/80134581
https://www.bilibili.com/video/av41218992?from=search&seid=1112733162576041562
gerapy 部署爬虫项目
http://www.cnblogs.com/yunlongaimeng/p/9818463.html
爬虫监控框架
https://cuiqingcai.com/6217.html
https://gitee.com/coliza/MongooCrawler