爬虫简介
- 爬虫定义:是一种按照一定规则,自动地抓取万维网信息的程序或脚本
- 两大特征:
- 能按作者要求下载数据
- 能自动在网络上流窜
- 三大步骤:
- 下载网页
- 提取正确的信息
- 根据一定规则自动跑到另外一个网页上并循环前两步内容
- 爬虫分类:
- 通用爬虫
- 专用爬虫(聚焦爬虫)
- Python网络包
- Python3.x:urllib, urllib3, httplib2, requests
urllib
-
包含模块:
- urllib.request:打开和读取urls
- urllib.error:包含urllib.request产生的常见错误,使用try捕捉
- urllib.parse:包含即系url的方法
- urllib.robotparse:解析robots.txt文件
-
网页编码问题解决
- charset:可以自动检测页面文件的编码格式,但可能有误
- 需要安装,命令:
conda install charset
-
urlopen的返回对象
- geturl:返回请求对象的url
- info:请求反馈对象的meta信息
- getcode:返回http的code
-
request.data的使用
- 访问网络的两种方法
- get
- 利用参数给服务器传递信息
- 参数为dict,然后用parse编码
- post
- 一般向服务器传递参数使用
- post是把信息自动加密处理
- 我们如果想使用post信息,需要用到data参数
- 使用post,意味着http的请求头可能需要更改:
- Content-Type: application/x-www.form-urlencode
- Content-Length: 数据长度
- 简而言之,一旦更改请求方法,请注意其他请求头部信息要与之相适应
- urllib.parse.urlencode可以将字符串自动转换成上面的格式
- 为了更好地设置请求信息,单纯的通过urlopen函数已经不太好用了,此时需要利用request.Request类
- get
- 访问网络的两种方法
-
urllib.error
- URLError产生的原因:
- 没网
- 服务器链接失效
- 找不到指定服务器
- 是OSError的子类
- HTTPError是URLError的一个子类
- 两者区别:
- HTTPError是对应HTTP请求的返回码错误,如果返回错误码是400以上的,则引发HTTPError
- URLError对应的一般是网络出现问题,包括url问题
- 关系区别:OSError->URLError->HTTPError
- URLError产生的原因:
-
UserAgent
- UserAgent:用户代理,简称UA,属于heads的一部分,服务器通过UA来判断访问者身份
- 常见的UA值,使用的时候可以直接复制粘贴,也可以用浏览器访问的时候抓包
- 设置UA可以通过两种方式
- headers
- add_header方法
-
ProxyHandler处理(代理服务器)
- 使用代理IP,是爬虫的常用手段
- 代理用来隐藏真实访问中,代理也不允许频繁访问某一个固定网站
- 使用步骤:
- 设置代理地址
- 创建ProxyHandler
- 创建Opener
- 安装Opener
-
cookie & session
-
由于http协议的无记忆性,人们为了弥补这个缺憾,所采用的一个补充协议
-
cookie是发放给用户(即http浏览器)的一段信息,session是保存在服务器上的对应的另一半信息,用来记录用户信息
-
cookie 和 session的区别
- 存放位置不同
- cookie不安全(即应该存放不敏感的信息)
- session会保存在服务器上一定时间,会过期
- 单个cookie保存数据不超过4k,很多浏览器限制一个站点最多保存20个
-
session的存放位置
- 存在于服务器端
- 一般情况,session是放在内存中或者数据库中
-
使用cookie登录
- 直接把cookie复制下来,然后手动放入请求头
- http模块包含一些关于cookie的模块,通过他们可以自动使用cookie
- CookieJar
- 管理存储cookie,向传出的http请求添加cookie
- cookie存储在内存中,CookieJar实例回收后cookie将消失
- FileCookieJar
- 使用文件管理cookie
- filename是保存cookie的文件
- MozillaCookieJar
- 创建与Mocilla浏览器cookie.txt兼容的FileCookieJar实例
- LwpCookieJar
- 创建与libwww-perl标准兼容的Set-Cookie3格式的FileCookieJar实例
- 关系:CookieJar->FileCookieJar->MozillaCookieJar & LwpCookieJar
- CookieJar
-
处理复杂的请求
# 生成cookie管理器 cookie_handler = request.HTTPCookieProcessor(cookie) # 创建http请求管理器 http_handler = request.HTTPSHandler() # 生成https管理器 https_handler = request.HTTPSHandler()
- 建立handler后,使用opener打开,打开后相应的业务由相应的handler处理
- cookie的属性
- name:名称
- value:值
- domain:可以访问此cookie的域名
- path:可以访问此cookie的路径
- expires:过期时间
- size:大小
- http字段
-
cookie的保存-FileCookieJar
-
cookie的读取
-
-
SSL
- SSL证书就是指遵守SSL安全套阶层协议的服务器数字证书(SercureSocketLayer)
- 美国网景公司开发
- CA(CertifacateAuthority)是数字证书认证中心,是发放、管理、废除数字证书的收信人的第三方机构
- 遇到不信任的SSL证书,需要单独处理
-
JS加密
- 有的反爬虫策略采用js对需要传输的数据进行加密处理(通常是MD5加密)
- 经过加密,传输的就是密文,但是加密函数或者过程一定是在浏览器中完成的,依旧是一定会js代码暴露给使用者
- 通过阅读加密算法,就可以模拟出加密过程,从而达到破解的效果
-
AJAX
- 异步请求
- 一定会有url,请求方法,可能有数据
- 一般使用json格式
-
Requests
-
HTTP for Humans,更简洁更友好
-
继承了urllib的所有特征
-
底层使用了urllib3
-
开源地址:https://github.com/kennethreitz/requests
-
中文文档:http://docs.python-requests.org/zh_CN/latest/index.html
-
安装命令:conda install requests
-
get请求
- requests.get(url)
- requests.request(“get”, url)
- 可以带有headers和parmas参数
-
get返回内容
-
post
- rsp = requests.post(url, data=data)
- data,headers要求是dict类型
-
proxy
- proxies = {
“http”: “address of proxy”
“https”: “address of proxy”
}
rsp = requests.request(“get”, “http://xxx”, “proxies=proxies”)
- proxies = {
-
用户验证
- 代理验证
- 可能需要用到HTTP basic Auth
- 格式:用户名:密码@代理地址:端口地址
- 例子:
proxy = {"http":"test1:123456@192.138.1.123:4444"} rsp = requests.get("http://xxx", prixies=proxy)
- 代理验证
-
web客户端验证
-
如果遇到web客户端验证,需要添加auth=(用户名,密码)
-
例子:
auth = ("test1", "123456") rsp = requests.get("http://www.baidu.com", auth=auth)
-
-
cookie
- requests可以自动处理cookie信息
- 例子:
rsp = requests.get("http://xxx") # 如果地方发武器传送过来cookie信息,则可以通过反馈的cookie属性得到 # 返回一个cookiejar实例 cookiejar = rsp.cookies # 可以将cookiejar转换为字典 cookiedict = request.utils.dict_from_cookiejar(cookiejar)
-
session
- 模拟一次对话,从客户端连接服务器开始,到客户端浏览器断开
- 能让我们跨请求时保持某些参数,比如在同一个session实例发出的,所有请求之间保持一个cookie
- 例子:
# 创建session对象,可以保持cookie值 ss = requests.session() headers = {'User-Agent': 'xxx'} data = {'name': 'xxx'} # 由创建的session管理请求 ss.post('http://xxx', data=data, headers=heade rs) rsp = ss.get('xxxx')
-
https请求验证ssl证书
- 参数verify表示是否需要验证ss证书,默认时True
- 如果不需要验证ssl证书,则设置成False
- 例子
rsp = requests.get("https://www.baidu.com", verify=False) # 如果用verify=True访问没有ssl证书的网站会报错
-
页面解析和数据提取
- 结构数据:先由结构,再谈数据
- JSON文件
- JSON Path
- 转换成Python类型进行操作(JSON类)
- XML文件
- 转换成Python类型(xmltodict)
- XPath
- CSS选择器
- 正则
- JSON文件
- 非结构数据:先有数据,再谈结构
- 文本、电话号码、邮箱地址…
- 通常处理此类数据,使用正则表达式
- Html文件
- 正则
- XPath
- CSS选择器
正则表达式
-
一套规则,可以在字符串文本中进行搜查替换等
-
正则使用方法:
- match:从开始位置查找,一次匹配
- search:从任何位置查找,一次匹配
- findall:全部匹配,返回列表
- finditer:全部匹配,返回迭代器
- split:分割字符串,返回列表
- sub:替换
-
匹配中文
- 中文unicode范围主要在[u4e00-u9fa5]
-
贪婪与非贪婪
- 贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配
- 非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配
- Python里面数量词默认是贪婪模式
- 例如:
- 查找文本abbbbbbccc
- re是ab*
- 贪婪模式:结果是abbbbbb
- 非贪婪模式:结果是a
-
XML(EXtensibleMarkupLanguage)
- 概念:父节点,子节点,先辈节点,兄弟节点,后代节点
-
XPath(XML Path Language)
-
是一门在XML文档中查找信息的语言
-
官方文档:http://www.w3school.com.cn/xpath/index.asp
-
XPath开发工具
- 开源的XPath表达式工具:XMLQuite
- Chrome插件:Xpath Helper
- Firefix插件:Xpath Checker
-
常用路径表达式
- nodename:选取此节点下的所有子节点
- /:从根节点开始选
- //:选取元素,而不考虑元素的具体位置
- .:当前节点
- …:父节点
- @:选取属性
- 案例
- booksotre:选取booksotre下的所有子节点
- /booksotre:选取根元素
- booksotre/book:选取booksotre下所有名字为book节点的子元素
- //book:选取book子元素
- //@lang:选取名称为lang的所有属性
-
谓语(Predicates)
- 谓语用来查找某个特定的节点,被镶嵌在方括号中
/booksotre/book[1]
:选取属于booksotre下叫book的第一个元素/booksotre/book[last()]
:选取属于booksotre下叫book最后一个的元素/booksotre/book[last()-1]
:选取属于booksotre下叫book倒数第二个的元素/booksotre/book[position()<3]
:选取属于booksotre下叫book的前两个元素/booksotre/book[@lang]
:选取属于booksotre下叫book且含有属性lang的元素/booksotre/book[@lang="cn"]
:选取属于booksotre下叫book且含有属性lang的值是cn的元素/booksotre/book[@price<90]
:选取属于booksotre下叫book且属性price的值小于90的元素/booksotre/book[@price<90]/title
:选取属于booksotre下叫book且属性price的值小于90的元素的子元素title
-
通配符
*
:任何元素节点@*
:匹配任何属性节点node()
:匹配任何类型的节点
-
选取多个路径
//book/title | //book/author
:选取book元素中的title和author元素//title | //price
:选取文档中所有的title和price元素
-
lxml库
- 局部遍历,速度较快
- python的HTML/XML解析器
- 官方文档:https://lxml.de/xpathxslt.html
- 功能
- 解析HTML
- 读取文件
- etree和XPath配合使用
BeautifulSoup4
- 全局遍历,速度较慢
- 官方文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/#
- 四大对象
-
Tag
- 对应HTML中的标签
- 可以通过soup.tag_name来定位标签
- Tag两个重要属性
- name:名称
- sttrs:属性
-
NavigableString
- 对应内容值
- string:内容值
-
BeautifulSoup
- 表示的是一个文档的内容,大部分可以把它当作tag对象
- 一般我们可以用soup来表示
-
Comment
- 特殊类型的NavagableString对象
- 对其输出,则内容不包括注释符号
-
遍历文档对象
- contents:tag的子节点以列表的方式给出
- children:子节点以迭代器形式返回
- descendants:所有孙子节点
-
搜索文档对象
- find_all(name, attrs, recursive, text, **kwargs)
- name:按照输入的字符串搜索,可以传入的内容有
- 字符串
- 正则表达式
- 列表
- attrs:用来表示属性
- text:对应tag的文本值
- name:按照输入的字符串搜索,可以传入的内容有
- find_all(name, attrs, recursive, text, **kwargs)
-
CSS选择器
- 使用soup.select,返回一个列表
- 通过标签名称:
soup.select("title")
- 通过类名:
soup.select(".content")
- 通过ID:
soup.select("#name_id")
- 组合查找:
soup.select("div #input_content")
- 属性查找:
soup.select("img[class='photo']")
- 获取tag内容:
tag.get_text
-
几个常用提取信息工具的比较
- 正则:很快,不好用,无需安装
- beautifulSoup:慢,使用简单,安装简单
- lxml:比较快,使用简单,安装一般
-
正则表达式
- 一套规则,可以在字符文本中进行搜查替换等
动态HTML
动态HTML介绍
- JavaScript
- jQuery
- Ajax
- DHTML
- Python采集动态数据
- 从JavaScript代码入手采集
- Python第三方库运行JavaScript,直接采集你在浏览器看到的内容
Selenium
-
Selenium:Web自动化测试工具
- 自动加载页面
- 获取数据
- 截屏
- 安装:pip install selenium
- 官网:https://www.seleniumhq.org/
-
Selenium库有一个WebDriver的API
-
WebDriver可以跟页面上的元素进行各种交互,用它可以来进行爬取
chrome + chromedriver
- chromedriver:是一个无界面的浏览器
- 好处:相比有界面的浏览器,它不需要渲染,所以爬取数据的效率更高
- Selenium操作主要分两大类
- 得到UI元素
- find_element_by_id
- find_elements_by_name
- find_elements_by_xpath
- find_elements_by_link_text
- find_elements_by_partial_link_text
- find_elements_by_tag_name
- find_elements_by_class_name
- find_elements_by_css_selector
- 基于UI元素操作的模拟
- 单击
- 右键
- 拖拽
- 输入
- 可以通过导入ActionsChains类来做到
- 得到UI元素
验证码问题
-
验证码:识别机器人或者爬虫
-
分类:
- 简单图片
- 极验:www.geetest.com
- 组合图片
- 电话
- google验证
-
验证码破解:
- 通用方法:
- 下载网页和验证码
- 手动输入验证号码
- 简单图片
- 使用图像识别软件或者文字识别软件
- 可以使用第三方图像验证码破解网站:www.chaojiying.com
- 极验:www.geetest.com
- 破解比较麻烦
- 可以模拟鼠标等移动
- 一直在进化
- 组合图片
- 放弃
- 电话
- 语音识别
- google验证
- 通用方法:
Tesseract
- 机器视觉领域的基础软件
- 但识别率不高
- OCR:OpticalChracterRecognition,光学文字识别
- Tessert:一个OCR库,有google赞助
- 安装:
- Windows:下载文件,放到Python目录下,设置环境变量
- Linux:apt-get install tesseract-ocr
- 安装完后还需要pytesseract
- pip install pytesseract
Scrapy爬虫框架
-
爬虫框架
- Scrapy
- pyspider
- crawley
-
Scrapy框架介绍
- https://scrapy-chs.readthedocs.io/zh_CN/latest/index.html
- https://scrapy.org/
-
安装
- pip install scrapy
-
概述
- 包含各个部件
- ScrapyEngine:神经中枢,大脑,核心
- Scheduler调度器:引擎发来的request请求,调度器需要处理,然后交换引擎
- Downloader下载器:对引擎发来的requests发出请求,得到response给Spider处理
- Spider爬虫:负责把下载器得到的网页/结果进行分解,分解成数据+链接,数据交给ItemPipeline,链接交交给Scheduler
- ItemPipeline管道,详细处理Item
- DownloaderMiddleware下载中间件:自定义下载的功能扩展组件
- 包含各个部件
-
爬虫程序大概流程
- 新建项目:scrapy startproject xxx
- 明确需要目标/产出:编写item.py
- 制作爬虫:地址spider/xxspider.py
- 存储内容:pipelines.py
-
ItemPipeline
- 对应的是pipelines文件
- 爬虫提取出数据存入item后,item中保存的数据需要进一步处理,比如清洗,去重,存储等
- pipeline需要处理process_item函数
- process_item:
- spider提取出来的item作为参数传入,同时传入的还有spider
- 必须返回一个Item对象,被丢弃的item不会被之后的pipeline处理
- init:构造函数
- 进行一些必要的参数初始化
- open_spider(spider)
- spider对象被开启的时候调用
- close_spider(spider)
- 当spider对象被关闭的时候调用
- process_item:
-
Spider
- 对应的是文件夹spiders下的文件
- init:初始化爬虫名称,start_urls列表
- start_requests:生成Requests对象交给Scrapy下载并返回response
- parse:根据返回的response解析出相应的item,item自动进入pipeline;如果需要,解析出url,url自动交给requests模块,一直循环下去
- start_request:此方法仅能被调用一次,读取start_urls内容并启动循环过程
- name:设置爬虫名称
- start_urls:设置开始第一批爬取的url
- allow_domains:spider允许爬取的域名列表
- start_request(self):只被调用一次
- parse
- log:日志记录
-
中间件(DownloaderMiddlewares)
- 中间件是处于引擎和下载器中间的一层组件
- 可以有很多个,按顺序加载执行
- 作用:对发出的请求和返回的结果进行预处理
- 在Middlewares文件中
- 需要在settings中设置以便生效
- 一般一个中间件完成一项功能
- 必须实现以下一个或多个方法
- process_request(self, request, spider)
- 在request通过时被调用
- 必须返回None或Response或Request或raise IgnoreRequest
- None:scrapy将继续处理该request
- Request:scrapy会停止调用process_request并冲洗调度返回的request
- Response:scrapy将不会调用其他的process_request或者process_exception,直接将该response作为结果同时调用process_response函数
- process_response(self, request, response, spider)
- 跟process_request大同小异
- 每次返回结果的时候会自动调用
- 可以有多个,按顺序调用
- process_request(self, request, spider)
-
去重
- 为了防止爬虫陷入死循环,需要去重
- 即在spider中的parse函数中,返回Request的时候加上dont_filter=False参数
-
如何在scrapy使用selenium
- 可以放入中间件的process_request函数中
- 在函数中调用selenium,完成爬取后返回Response
scrapy-shell
- 启动
- Linux:ctrl+T,打开终端,输入scrapy shell “url:xxx”
- windows:打开Anaconda Prompt,输入scrapy shell “http://xxx”
- 启动后自动下载指定url的网页
- 下载完成后,url的内容保存在response变量中,如果需要,可以调用response
- response
- 爬取到的内容保存在response中
- response.body时网页的代码
- response.headers是返回的http的头信息
- response.xpath()允许使用xpath语法选择内容
- response.css()允许使用css语法选取内容
- selector
- 选择器,允许用户使用选择器来选择自己想要的内容
- response.selector.xpath:response.xpath是selector.xpath的快捷方式
- response.selector.css:response.css是它的快捷方式
- selector.extract:把节点的内容用unicode形式返回
- selector.re:允许用户通过正则选取内容
分布式爬虫
- 单机爬虫的问题
- 单机效率
- IO吞吐量
- 多爬虫问题
- 数据共享
- 在空间上不同的的多台机器,可以成为分布式
- 解决方法
- 共享队列
- 去重
- 共享队列-Redis
- 内存数据库
- 同时可以落地保存到硬盘
- 可以去重
- 可以对保存的内容进行生命周期控制
- 可以把它理解成一个dict, set, list的集合体
- 内容保存数据库
- MongoDB
- MySQL等传统关系数据库
- 安装scrapy_redis
- pip install scrapy_reids
- github.com/rolando/scrapy-redis
- https://scrapy-redis.readthedocs.io/en/stable