爬虫总结


P6笔记


文章目录

    • @[toc]
    • 1.虚拟环境
        • 1.1 虚拟环境管理模块
        • 1.2 环境一致性
        • 1.3 查看包的详细信息
        • 1.4 打包
    • 2.爬虫
        • 2.1 爬虫的概念
          • 2.1.1 `pyinstaller`
        • 2.2 通用爬虫
        • 2.3 robots协议
        • 2.4 聚焦爬虫
    • 2.5 requests模块
        • 2.6 OSI七层模型
        • 2.7 TCP/IP五层模型
        • 2.8 TCP和UDP
        • 2.9 ARP协议
          • 2.9.1 ssh
          • 2.9.2 服务器创建的默认端口
          • 2.9.3 `http`与`HTTPS`协议的区别
    • 3.请求
        • 3.1 请求过程
        • 3.2 请求方法
        • 3.3 请求头和请求体
        • 3.4 反爬机制与反反爬策略
    • 4.响应
        • 常见的状态码
        • 4.1 响应数据的几种形式
        • 4.2 uuid
    • 5.正则表达式
        • 5.1 元字符
        • 5.2 字符组
        • 5.3 量词
        • 5.4 边界修饰
        • 5.5 分组
        • 5.6 贪婪与非贪婪
        • 5.7 re
    • 6. requests高阶应用
        • 6.1 文件处理
        • 6.2 会话维持
        • 6.3 `ssl`证书验证
        • 6.4 代理设置
        • 6.5 超时设置
        • 6.6 `UA`检测
        • 6.7 cookie的处理(session)
    • 7.`lxm`库
        • 7.1 `xpath`基本语法
        • 7.2 属性匹配
        • 7.3 按序选择
        • 7.4 流程
        • 7.5 补充
    • 8.动态数据加载
        • 8.1 selenium
        • 8.2 selenium安装与配置与操作
        • `iframe`标签跳转
        • 8.3 子级页面和父级页面
        • 8.4 防检测
    • 9.多线程爬虫
        • 9.1 并发与并行
        • 9.2 示例
    • 10.无头浏览器与`BS4`
        • 10.1 无头浏览器
        • 10.2 `BS4`语法
    • 11.快代理网站的模拟登录
        • 11.1 第三方打码平台
    • 12.`Scrapy`框架
        • 12.1 安装
        • 12.2 创建项目
        • 12.3 运行项目
        • 12.4 项目基本架构
        • 12.5 核心组件与数据流向
          • 核心组件
          • 数据流向
        • 12.6 组件分析
        • 12.7 保存数据
        • 12.8 管道(`Item Pipline`)的使用
        • 12.9 整体流程
    • 13.`MongoDB`数据库
        • 13.1 数据库操作
        • 13.2 增加数据
        • 13.3 查询数据
        • 13.4 修改数据
        • 13.5 删除数据
    • 13.`MongoDB`数据库
        • 13.1 数据库操作
        • 13.2 增加数据
        • 13.3 查询数据
        • 13.4 修改数据
        • 13.5 删除数据

公开课

1.虚拟环境

虚拟环境就是一个隔离的python环境,不同的项目应该使用不同的虚拟环境(可以使用同一个虚拟环境)

虚拟环境不会导致环境之间的污染

1.1 虚拟环境管理模块

virtualenvwrapper

安装

pip install virtualenvwrapper-win

virtualenvwrapper的使用

查看所有虚拟环境:lsvirtualenv

创建虚拟环境:mkvirtualenv 环境名

激活虚拟环境:workon 环境名

查看当前虚拟环境下的模块:

  1. 进入当前虚拟环境
  2. pip list

退出虚拟环境:deactivate

虚拟环境中安装模块:pycharm中,选中虚拟环境,然后添加模块,也可以通过pip install安装

删除虚拟环境:rmvirtualenv 环境名

pycharm如果没有直接显示虚拟环境,则
settings中选解释器处show all ,加号添加,创建的虚拟环境在
C:\Users\22742\Envs\目录下,选中Scripts下的python.exe

1.2 环境一致性

保证开发与生产环境一致,需要将模块等同一致

在开发机的虚拟环境中,运行命令:
生成模块和其版本pip freeze > requirements.txt

requirements中生成的模块版本进行安装
pip listall -r ./requirements.txt

1.3 查看包的详细信息

pip show 包名

1.4 打包

pyinstall -F XXX.py


2.爬虫

2.1 爬虫的概念

爬虫又称网页蜘蛛或者网页机器人

模拟人操作客户端,向服务器发起网络请求,抓取数据的自动化程序和脚本

通用爬虫是通过抓取数据实现检索服务

爬虫分为聚焦爬虫和通用爬虫

自动化,数据量较小时可以人工获取数据,但往往在公司中爬取的量都在百万级千万级,所以要程序自动化获取数据

B/S架构:Browser/Server,类似淘宝,没有第三方中转,客户端和服务器直接交互

C/S:Client/server,类似微信,将微信后端作为中转站,和其他人对话时,需要在中转站传话

2.1.1 pyinstaller

pyinstaller可以将python文件编译成一个程序,类似go语言的编译

2.2 通用爬虫

百度,360,搜狐等搜索引擎

原理:

  1. 抓取网页
  2. 采集数据
  3. 数据处理
  4. 提供检索服务

通用爬虫抓取新网站的方式

  1. 主动提交url
  2. 设置友情连接
  3. 百度会和DNS服务商合作,抓取新网站

检索排名:

  1. 竞价排名
  2. 根据PageRank值,访问量、点击量 (SEO)

2.3 robots协议

robots.txt:如果不想让百度爬取,可以编写robots.txt,这个协议只是口头上的协议,自己写的爬虫程序不需要遵从

2.4 聚焦爬虫

根据特定的需求,抓取指定的数据

思路:

代替浏览器上网

  1. url,发起请求,获取响应
  2. 解析内容,提取数据
  3. 将数据存储到本地,数据持久化

2.5 requests模块

#导包
import requests
url = '*****'
#res是获取的响应数据
res = requests.get(url)
#响应数据的获取方式
1.文本形式:res.text
2.json形式:res.json()
3.流形式:res.content
#数据持久化(mysql入库)
#1.导包
import pymysql
#2.创建链接
conn = pymysql.connect(host='127.0.0.1',port=3306,user='root',password='root',charset='utf8',database='***')
#3.创建游标
cursor = conn.cursor()
#4.构造sql语句
sql  = 'insert into *** values(数据)'
#5.执行sql语句
try:
    cursor.execute(sql)
    #提交事务
    conn.commit()
except Exception as e:
    print(e)
    #回滚
    conn.rollback()

params参数

get方式传参的拼接,将参数拼接到目标url

2.6 OSI七层模型

应用层:

https/http/ftp

http协议:明文传输,端口80
https协议:加密传输,端口443

表示层

会话层

传输层:UDP/TCP

网络层:IP

数据链路层:ARP

物理层:以太网协议

2.7 TCP/IP五层模型

应用层:https/http/ftp/ssh/Sftp/

传输层:UDP/TCP

网络层:IP

数据链路层:ARP

物理层:以太网协议

2.8 TCP和UDP

TCP协议是一种面向连接的,可靠的,基于字节流的传输通信协议

  1. 有序性:数据包编号,判断数据包的正确次序
  2. 正确性:使用checksum函数检查数据包是否损坏,发送和接收时都会计算校验和
  3. 可靠性:发送端由超时重发,并有确认机制识别错误和数据的丢失
  4. 可控性:滑动窗口协议拥塞控制算法控制数据包的发送速度

UDP协议是用户数据报协议,面向无连接的传输层协议,传输相对于TCP来说,不可靠

  1. 无连接:数据可能丢失或损坏
  2. 报文小,传输速度快
  3. 吞吐量大的网络传输,可以在一定成都上承受数据丢失

2.9 ARP协议

通过IP获取目标计算机的mac地址的协议

交换机不能识别IP地址

2.9.1 ssh

远程登录会话

2.9.2 服务器创建的默认端口
ftp:21
ssh:22
mySQL:3306
MongoDB:27017
Redis:6379
2.9.3 httpHTTPS协议的区别
  1. https协议需要到ca申请证书,因而需要一定费用,现阶段国内各大厂商也提供免费的证书
  2. http是超文本传输协议,信息是铭文传输,https则是具有安全性的ssl加密传输协议
  3. httphttps使用的是完全不同的连接方式,端口号也不一样,前者是80,后者是443
  4. http的连接很简单,是无状态的,https协议是由ssl+http协议构建的可进行加密传输,身份认证的网络协议,比http协议安全(尽管HTTPS安全,但是传输的效率没有http高)

3.请求

url:请求的网址,即统一资源定位符,它可以唯一确定我门向请求的资源

3.1 请求过程

客户端,通常指(web浏览器或APP)向服务器发起请求,服务器接收到的请求进行处理,并向客户端发起响应

请求由客户端向服务器发出的,可以分为四部分:请求方法(Request Method),请求网址(Request.URL),请求头(Request Headers) 请求体(Request Body)

3.2 请求方法

常见的有八种

GET:请求页面,并返回内容
POST:用于提交表单数据或者文件等,数据包含在请求体中
PUT:从客户端向服务器传送的数据取代指定文档中的内容
DELETE:请求服务器删除指定的页面
HEAD:类似于GET请求,只不过返回的响应中没有具体的内容,用于获取头
CONNECT:把服务器当跳板,让服务器代替客户端访问其他网页
OPTIONS:允许客户端查看服务器的性能
TRACE:会现实服务器收到的请求,主要用于测试或诊断

GET和POST请求的区别

  1. GET请求中的参数包含在URL里,数据可以在URL中看到,而POST请求的URL一般不会包含这些数据
  2. GET请求提交的数据最多只有1024字节,而POST方法没有限制
  3. POST比 GET相对安全

3.3 请求头和请求体

Accept:请求报头域,用于指定客户端可接收哪些类型的信息

Cookie:页常用复数形式Cookies,这是网站为了辨别用户进行会话跟踪而存在用户本地的数据,它的主要功能时维持当前访问会话,cookies里有信息标识了我们所对应的服务器的会话,每次浏览器在请求该站点的页面时,都会在请求头中加上cookies并将其发送给服务器,服务器通过cookies识别出是我们自己,并且查出当前是登录状态,所以返回的数据是登录之后网页内容

Referer:此内容用来标识这个请求是从哪个页面发过来的,服务器可以拿到这一信息并做相应的处理,如来源统计,防盗链处理等

User-Agent:简称UA,它是一个页数的字段串头,可以使服务器识别客户使用的操作系统及版本,浏览器及版本等信息,做爬虫时加上此信息,可以伪装浏览器

x-requested-with:XMHttpRequest 代表ajax请求

Accept-Language:指定客户端可接受的语言类型

Accept-Encoding:指定客户端可接受的内容编码

Content-type:也叫互联网媒体类型(Internet Media Type)或者MIME类型,在HTTP协议消息头中,它用来表示具体请求中的媒体类型信息,例:text/html代表HTML格式,image/gif代表GIF图片,application/json代表JSON类型

请求体一般承载的内容时POST请求中的表单数据,GET请求没有请求体,为空

3.4 反爬机制与反反爬策略

#反爬机制:
为了不让数据泄露,设置了各种阻碍,这就是反爬机制

#反反爬策略
针对网站的反爬机制,采取不同策略
1.脚本:直接忽略
2.scrapy框架:修改配置文件,让爬虫不遵守robots协议

4.响应

响应是由服务端返回给客户端的,可 以分为三部分:响应状态码,响应头,响应体

响应体,响应的正文数据都在响应体中,比如请求网页时,它的响应体就是网页的HTML代码,我们要爬虫请求网页后,要解析的内容就是响应体

常见的状态码

200:成功
301:永久重定向
302:临时重定向
400:错误的请求
401:未授权
403:服务器拒绝此请求
404:未找到
500:服务器内部错误
501:服务器不具备完成请求的功能
502:错误的网关,服务器走位网关或代理,从上游服务器收到无效响应
504:网关超时,服务器作为网关或代理,但是没有及时从上游服务器收到请求
505:HTTP版本不支持

状态码不能完全代表相应状态,部分网站的状态码是自定义的,一切以响应数据为准

4.1 响应数据的几种形式

res = requests.get(url='https://www.guidaye.com/cp/')
res.text >>>将响应对象转化为str类型
res.json()  >>> 将响应对象转化为python中的dict类型,形式(类json)
res.content  >>>流形式(数据流,图片就是流形式)

如果响应数据中文乱码,可以用content.decode('utf-8')来解决

4.2 uuid

通用唯一标识符,时间戳,命名空间,随机数,伪随机数来保证生成ID的唯一性

python的uuid模块提供UUID类和函数uuid1(), uuid3(),uuid4(),uuid5() 来生成1, 3, 4, 5各个版本的UUID
( 需要注意的是: python中没有**uuid2()**这个函数)

uuid1:基于时间戳
uuid3:基于名字的MD5散列值
uuid4:基于随机数,有一定重复概率
uuid5:基于名字的SHA=1散列值

5.正则表达式

5.1 元字符

.:任意字符,换行符除外
\d:任意数字
\w:任意数字字母下划线
\s:空白符

#如果是大写的s,w,d,代表'非'

5.2 字符组

[a-z]:
[A-Z]:
[0-9]:

[^...]
#匹配非其中元素,举例:[^abc]--->匹配除了abc之外的字符

5.3 量词

*:匹配0次或多次
+:匹配1次或多次
?:匹配0次或1#非贪婪匹配
{m}:m次
{m,}:至少m次
{m,n}:m-n次
{,n}:最多n次

5.4 边界修饰

^匹配开始

$匹配结尾

5.5 分组

import re
s = ""

res = re.findall(r"href='(.*?)'>",s)

5.6 贪婪与非贪婪

贪婪,尽可能多的匹配

非贪婪,尽可能往少了匹配

5.7 re

re.findall(r'正则表达式','str'),结果是一个列表,匹配整个字符串

re.search(r'正则表达式','str')匹配到第一个结果就返回,返回的是一个对象,使用group取值

re.match(r'正则表达式','str')从字符串开始进行匹配,返回一个对象,使用group取值,如果未匹配到,返回None

re.complie将正则表达式编译为对象,在需要按正则表达式匹配是可以在直接使用该对象调用以上方法


6. requests高阶应用

6.1 文件处理

import requests
#打开文件,注意要以rb形式打开
f = open('chn.jpg','rb')
files = {
    'file':f
}
res = requests.post(url='***',files = files)

文件也是一种数据,所以,可以通过files参数来进行文件的上传

6.2 会话维持

from requests import Session
#1.实例化一个对象
session = Session()
#2.url
url = '*****'
#3.session.get()或者session.post(url=url.headers=headers)
res = session.post(url=url.headers=headers)

6.3 ssl证书验证

httpshttp的安全版本,HTTPShttp的基础上多了一个ssl安全套接层

requests提供了证书验证的功能,当发起HTTP请求时,模块会检查SSL证书,但检查的行为可以用verify参数来控制

添加了一个参数verify=false --->不检查ssl证书,如果等于True,则检查SSL证书

#ssl证书验证

#添加一个verify=false参数,禁止证书验证
import requests
url = '******'
#阻止抛出警告
requests.packages.urllib3.disable_warinings()

res = requests.get(url=url,verify=false)

简单来说,在爬取网站时,有可能网站的证书是有问题的,这时如果使用requests模块去请求时,会报错,所以需要ssl证书验证

6.4 代理设置

代理IP是指在请求的过程中使用非本机ip进行请求,避免大数据量频繁请求的过程中出现IP封禁,限制数据的爬取

透明代理ip:服务器知道你使用了代理,服务器能够获取爬虫真实的ip

匿名代理ip:服务器知道你使用了代理,服务器不能获取爬虫真实的ip

高匿代理ip:服务器不知道使用了代理,服务器不能获取爬虫真实ip

代理类别:基于接口的,基于隧道的

反爬:ip封禁--->使用代理ip

import requests
url = '*********'
proxies = {
    #或者是https
    'http':'http://ip地址:端口号',
    #无论是http还是https,后面一定是http
    'https':'http://ip地址:端口号'
}
res = requests.get(url=url,proxies = proxies)

6.5 超时设置

添加了一个参数,以计量timeout=0.1

#添加timeout参数,秒数
import requests
res=requests.get(url=url,timeout=0.1)

给予爬虫与服务器连接的时间限定,设置一个时间,在指定的时间内完成了正常的连接,不报错,如果没有完成,就会报错

requests模块发送请求可以设置超时时间,在超时时间内未得到响应,便会抛出异常

好处:一方面减少了请求的阻塞时间,一方面,可以进行异常处理,执行相应的操作

如果规定时间完成了和服务器连接,之后爬取数据的时间并不算在超时设置的时间内

6.6 UA检测

UA是用户的身份表示,可以表示用户的系统及浏览器信息

在请求过程中,添加headers参数

6.7 cookie的处理(session)

在同一个关联网页中,为了保存登录状态和各种信息,可以通过cookie来保持

三种方法

  1. 手动在headers中添加cookie的键值对
  2. cookiejar对象
  3. 自动封装cookie的类:Session
#cookie的处理
	#1.url = 'https://www.baidu.com/'
        headers = {
            'Cookie':'BIDUPSID=B63BDB40304991E9FF3159864CC9C302; PSTM=1586308511; BAIDUID=B63BDB40304991E9CC4E4ECFFCFFB23D:FG=1; BD_UPN=12314753; BDUSS=VWNmZu',
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'
        }
        res = requests.get(url=url,headers=headers)
        
    #2.cookiejar对象
    from requests.cookies import RequestCookieJar
   	1.首先需要获取Cookies
    Cookie = ***********
    2.实例化一个jar对象
    jar = RequestsCookieJar()
    3.处理Cookies,封装进jar对象中
    for i in Cookie.split(','):
        #再次分割,分成dict的键值,每分割一次添加一次
        k,v = i.split('=',1)
        jar.set(k,v)
    
    #3.Session类,会话维持
    from requests import Session
    1.实例化一个对象
    session = Session()
    2.url
    url = '*****'
    3.session.get()或者session.post(url=url.headers=headers)
    res = session.post(url=url.headers=headers)
from requests import Session

session = Session()

data = {
    'username':"天听",
    'password':'123456'
}

res = session.post(url=url,headers=headers,data=data)

7.lxm

从响应数据中抽取出目标数据的过程,就叫做数据解析
数据解析:
re,xpath,BS4,Pyquery

DOM树与xpath解析原理

HTML页面标签存在层级关系,即DOM树,在获取目标数据时可以根据网页层级关系定位标签,再获取标签的文本或属性

xpath解析原理:根据DOM节点的结构关系,进行定位

7.1 xpath基本语法

.:当前节点

/:根节点

//:代表任意位置

.//:从当前节点向下的任意位置匹配

nodename:节点名定位

nodename[@attribute='value']:根据节点的属性进行定位

@attribue:获取节点的属性值,比如获取a标签的href属性,直接可以/a/@href

text():获取节点的文本
//div[@class='asdds']/p/text()

7.2 属性匹配

  • 单属性多值匹配:当节点的一个属性有多个值时,根据其中一个进行定位,使用contai ns函数

    '//div[contains(@class,"属性值")]'
    
  • 多属性匹配:用节点的多个属性共同定位节点and

    '//div[@class="asds" and @name="adsadasd"]'
    

7.3 按序选择

  • 索引定位:[6]

    注意,索引从1开始,跟python有区别

  • 位置函数:position
    例:/li[position()>2]

  • last()函数:定位最后一个,last()-1代表倒数第二个

7.4 流程

加载本地html,需要有etree.HTMLParser参数,注意要加括号
例:tree = etree.parser('./xpath.html',etree.HTMLParser())

加载网页html,直接使用HTML
例:tree = etree.HTML()

然后些xpath语法
tree.xpath('//ul[@class="pli"]/li/div/a/img/@src')

xpath获得的结果是一个列表2

#编码流程
from lxml import etree
res = requests.get(...)
tree = etree.HTML(res.text) #etree加载的是响应数据的文本形式
tree.xpath('xpath表达式')

7.5 补充

res1 = tree.xpath("//div[@id='007']/text()")

res2 = tree.xpath("//div[@id='007']//text()")

'''
res1展示的是以divid007为根节点的结果,其div下的其他标签不显示
res2展示的是以divid007任意位置的结果,其div下的其他标签内容也一同显示
'''

8.动态数据加载

网页HTML上,有些数据是通过js代码填充,所以如果直接使用爬虫,只会爬取到一个标签,并没有其中的元素

requests模块和scrapy框架在发起请求爬取数据的过程中,不能执行js代码

8.1 selenium

selenium是一个web端自动化测试框架,可以通过代码来控制浏览器,比如打开关闭,点击等行为

作用:帮助抓取动态加载的数据,避免反爬

8.2 selenium安装与配置与操作

  1. Chrome浏览器
    2.selenium框架:pip install selenium

3.驱动程序:下载
http://npm.taobao.org/mirrors/chromedriver/
查看浏览器版本
选择对应的版本

编码流程:
	#首先需要将下载的chromedriver.exe放到代码文件夹下
    #导包
    from seleniumi import webdriver
    
    
    #调用chromedriver.exe
    bro = webdriver.Chrome('./chromedriver.exe')
    
    #访问
    bro.get('https://www.iqiyi.com/')
    
    #获取网页源代码
	(对象)bro.page_source   --->字符串
#如何获取网页的元素
#根据标签内属性定位,一般用id定位
find_element_by_id('id')
find_element_by_name('name')
find_element_by_class_name('class')#根据class属性定位
find_element_by_xpath()#根据xpath定位节点
find_element_by_css_selector()#css选择器
find_element_by_link_text()#根据超链接文本定位
find_element_by_partial_link_text()#根据超链接文本的一部分定位

#执行js脚本
execute_script(js)

#节点交互操作:
1.输入内容:对象.send_keys()

2.清空内容:对象.clear()

3.点击操作:对象.click()

4.退出浏览器:对象.quit()

#获取网页的数据
获取元素属性:get_attribute()
获取元素文本:get_text()
获取元素位置:element.location
获取元素尺寸:element.size
获取网页源码:browser.page_source(*****)


#执行js脚本
js = 'window.scrollTo(0,300)'#向下滚动300距离

js = 'window.scrollTo(0,document.body.scrollHeight)'#滚动到底部


对象.execute_script(js)

iframe标签跳转

switch_to.frname('frameid')

switch_to.default_content()

实例:

from selenium import webdriver
from time import sleep
from selenium.webdriver.chrome.options import Options

options = Options()

options.add_experimental_option('excludeSwitches',['enable-automation'])

#调用chromedriver.exe
bro = webdriver.Chrome('./chromedriver.exe',options=options)

bro.get('https://www.baidu.com/')

#根据id-->kw获取input输入框
input_tag = bro.find_element_by_id('kw')

#根据id-->su获取百度一下点击按钮
button_baidu = bro.find_element_by_id('su')

#输入框输入
input_tag.send_keys('黑洞')

#点击
button_baidu.click()

#睡眠两秒之后清空输入框
sleep(2)
input_tag.clear()

input_tag.send_keys('抖动')
button_baidu.click()
sleep(3)
input_tag.clear()


input_tag.send_keys('翻转')
button_baidu.click()
sleep(3)
input_tag.clear()

bro.quit()

8.3 子级页面和父级页面

HTML页面嵌套另一个HTML页面

#子页面的跳转
switch_to.frame('id')

#跳转到父级页面
switch_to_default.conent()

注意,selenium默认操作父级页面

8.4 防检测

from selenium.webdriver.chrome.options import Options
options = Options()
options.add_experimental_option('excludeSwitches',['enable-automation'])
bro = webdriver.Chrome('./chromedriver.exe',options=options)

9.多线程爬虫

在爬取数据量大的数据时,耗费时间较长,为了提高效率,可采用多线程爬虫,提高效率,但是,多线程不是原子性,操纵数据可能会导致数据紊乱,不安全

9.1 并发与并行

并行:在同一时刻,多个任务同时执行

并发:在同一时间段内,多个任务同时执行
并发时,一般采用了时间片轮转法,即在一个时间段内,给每个程序添加一个时间片,也可以叫做进度条,进度条走完,下一个程序再继续运行,不过时间片轮转时,停顿时间非常的短,所以会造成多个任务同时运行的错误

9.2 示例

from threading import Thread
from queue import Queue
import requests
from lxml import etree
import pymongo
from threading import Lock

#负责爬取的类
class SpiderThread(Thread):
    def __init__(self, name, url_queue, data_queue):
        super().__init__()
        self.name = name
        self.url_queue = url_queue
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36'
        }
        self.data_queue = data_queue

    def run(self):
        base_url = 'http://xiaohua.zol.com.cn/lengxiaohua/%s.html'
        while 1:
            try:
                page = self.url_queue.get(block=False)
                print('%s正在爬取数据' % self.name )
                res = requests.get(url=base_url % page, headers=self.headers)
                self.data_queue.put(res.text)
            except:
                break

#负责解析的类
class ParseThread(Thread):
    def __init__(self, name, data_queue, lock):
        super().__init__()
        self.name = name
        self.data_queue = data_queue
        self.lock = lock

    def run(self):
        # 调用解析方法
        while 1:
            try:
                html = self.data_queue.get(block=False)
                print('%s 正在解析数据' % self.name)
                self.parse(html)
            except:
                break

    def parse(self, html):
        tree = etree.HTML(html)
        li_list = tree.xpath('//li[@class="article-summary"]')
        for li in li_list:
            title = li.xpath('.//span[@class="article-title"]/a/text()')
            content = ''.join(li.xpath('.//div[@class="summary-text"]//text()'))
            if title and content:
                data = {
                    'title': title[0],
                    'content': content
                }
                with self.lock:
                    self.save(data)

    def save(self, data):
        # 简历连接
        conn = pymongo.MongoClient()
        db = conn.lilong
        table = db.liuyueyang
        table.insert_one(data)

10.无头浏览器与BS4

10.1 无头浏览器

什么是无头浏览器(headless browser),简单来说是一种没有界面的浏览器。既然是浏览器那么浏览器该有的东西它都应该有,只是看不到界面而已。我们日常使用浏览器的步骤为:启动浏览器、打开一个网页、进行交互。而无头浏览器指的是我们使用脚本来执行以上过程的浏览器,能模拟真实的浏览器使用场景。

from selenium import webdriver
from selenium .webdriver.chrome.options import Options

chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--dissble-gpu')
bro = webdriver.Chrome(chrome_options = chrome_options)
bro.get(******)
print(bro.page_source)

10.2 BS4语法

编码流程:

  1. 导包from bs4 import BeautifulSoup
  2. 实例化对象,传两个参数,一个文本,一个解析器,一般为lxml
    suop = BeautifulSoup(res.text,'lxml')
  3. 选择器解析
#bs4编码流程
from bs4 import BeautifulSoup
suop = BeautifulSoup(res.text,'lxml')
tag = soup.select('css选择器表达式')
tag = soup.节点() #节点选择器
tag = soup.findall() # 方法选择器
#节点选择器
from bs4 import BeautifulSoup
soup = BeautifulSoup(res.text,'lxml')
tag = soup.a  #取a标签,只取一个
#方法选择器
find_all(name,attrs,text,limit):
    soup.findall(name='***')  #根据节点名字定位
    soup.findall(attrs={'属性名(scr,class等)':'值'})#根据属性定位,多个属性时,一个即可定位
    soup.findall(text=res.compile(r'***'))#根据节点文本定位,返回文本
    soup.findall(name='***',limit=2)#只返回两个结果
    
find(name,attrs,text,limit):区别于find_all,find返回的是一个对象结果
    
find_all(name=节点名,{attrs:属性值})返回的是一个列表
#css选择器
属性选择器:
1.根据节点名定位标签:标签选择器
soup.select('***(title,a,p等)')
2.根据节点的class属性定位:css选择器
soup.select('.***')
3.根据id定位
soup.select('#***')
4.嵌套选择:
ss = soup.select('ul')#得到的是一个列表
for i in ss:
    print(i.select("li"))
5.层级选择器
soup.select('div > ul > li') #单层级选择器,按照顺序找到直属li
soup.select('div li') #多层级选择器,包含了div下的所有li

#获取节点的文本或属性
obj.string:获取直接子文本,如果节点内有平行的节点,则结果是None
obj.get_text():获取子孙节点的所有文本
obj['***(属性)']:获取节点属性

11.快代理网站的模拟登录

from requests import Session

#实例化session对象
session = Session()
#登录的url
url = 'https://www.kuaidaili.com/login/'
#构造数据
data = {
    'next': '',
    'kf5_return_to': '',
    'username': '[email protected]',
    'passwd': 'o66.'
}
#浏览器头
headers = {
    "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36'
}

#用post请求将数据传进去,此时,已经模拟了登录,访问这个网站的其他网页时,会保存登录状态
res = session.post(url=url,headers=headers,data=data)

#访问时,可以获得到返回的数据,用户名在其中,模拟登录完成
ret = session.get(url='https://www.kuaidaili.com/api/checkuser/',headers=headers)
print(ret.json())

11.1 第三方打码平台

我们在模拟登录时,时常会遇到一些验证码代码无法准确的识别不同的验证码,这时,就用到了打码平台,它会将图片上的字符或者数字转成字符串返回给你

流程:

  1. 下载验证码图片
  2. 传给第三方打码平台
  3. 进行识别,识别完成之后,传回ret
  4. ret拿回,构造数据

超级鹰平台

12.Scrapy框架

1、scrapy框架
scrapy框架是基于 Twisted 异步框架的爬虫框架,具有异步性,爬虫的效率很高。
scrapy 框架用于爬取结构化数据。

2.scrapy框架的安装与配置
lxml  wheel Twisted pywin32 scrapy 

3.创建与运行
创建:
	1.scrapy startproject 项目名
	cd 项目名
	scrapy genspider 爬虫名 域名
运行:
	scrapy crawl 爬虫名(爬虫类 里的name)

scrapy是基于twisted的异步框架

12.1 安装

首先需要安装相应的依赖库

lxmlwheelpywin32

twisted此依赖安装时,需要从地址下载相应的版本,然后通过pip来安装

最后安装scrapy框架(目前自己使用 scrapy==1.8.0)

12.2 创建项目

创建项目
scrapy startproject 项目名

创建爬虫文件
scrapy genspider 爬虫名 域名

12.3 运行项目

scrapy crawl 爬虫名

12.4 项目基本架构

└── day01(A) 外层项目目录
└── day01(A) 内层项目目录
└── spiders 放置爬虫的包
└── __init__.py
└── tianting.py 爬虫文件

​ └── items.py 定义要爬取的数据字段
​ └── middlewares.py 中间件
​ └── piplines.py 管道
​ └── settings.py 配置文件(爬虫配置)
​ └── scrapy.cfg 配置文件,跟部署相关

12.5 核心组件与数据流向

核心组件

五大核心组件

1.引擎(Scrapy Engine):
整个框架的调度,负责各个组件之间的通信与数据的传递

2.爬虫(Spiders):
定义爬取行为和解析规则

3.调度器(Scheduler):
负责调度所有请求

4.下载器(Downloader):
负责爬取页面(与互联网交互,爬取页面的)

5.管道(Item Pipeline):
负责数据持久化

由于引擎负责各个组件之间的调度,所以,所有的组件在相互传递时,都需要经过引擎,比如,爬虫解析后需要将req给引擎,然后引擎再给调度器,注意,此时,调度器无法越过引擎去直接调用下载器,所以,需要将req再次返回给引擎,由引擎来调度下载器和互联网交互

数据流向

依据请求的生命周期

​ 【调度器】

​ ↑ ↓
2.req 3.req 4.req

【管道】 【引擎】 → 【下载器】 5.reqinternet
7.res6.res

​ ↑ ↓
1.req 8.res

​ 【爬虫】

9.爬虫经过引擎到达管道

12.6 组件分析

  • 爬虫组件

scrapy.Spider:Spider爬虫类,自建的爬虫类必须继承这个

将数据item在组件中传递,注意不是return,而是yield

class BlogSpider(scrapy.Spider):
    #爬虫名,爬虫唯一的身份标识,不可重复
    name = 'blog'
    #域名的限定,限制了爬虫的范围,可以注释
    # allowed_domains = ['baidu.com']
    #起始url,当项目启动,自动对这个url发起请求
    start_urls = ['http://baidu.com/']
    #parse是默认的解析回调方法,如果发送一个请求,未指定回调解析,默认调用parse
    def parse(self,response):
        li_list = response.xpath('//ul[@id="menu-list"]/li')
        for li in li_list:
            item = Test01Item()
            #名字
            item['title'] = li.xpath('.//h2/a/@title').extract_first()
            #简介
            item['brief'] = ''.join(li.xpath('./text()').extract()).replace('\n','')
            #时间
            item['date'] = re.findall(r'\d+-\d+-\d+', li.xpath('.//p/text()').extract_first())[0]
            #链接
            item['link'] = li.xpath('.//h2/a/@href').extract_first()
            yield item
  • item

Item 是保存爬取数据的容器,它的使用方法和字典类似,Item 多了额外的保护机制,可以避免拼写错误或者定义字段错误。
Item需要继承scrapy.Item类,并且定义类型为scrapy.Field字段

注意!!items只能够通过字典的方式进行访问和添加

在爬虫组件中使用item容器,需要以下4步:

  1. 导包,将items导入
  2. 实例化items中的类对象
  3. 通过键值对的字典形式将数据添加进items
  4. 通过yield将items返回
class Test01Item(scrapy.Item):
    title = scrapy.Field()
    jianjie = scrapy.Field()
    
#爬虫组件中:
item = Test01Item()
            #名字
item['title'] = li.xpath('.//h2/a/@title').extract_first()
    #简介
item['brief'] = ''.join(li.xpath('./text()').extract()).replace('\n','')
    #时间
item['date'] = re.findall(r'\d+-\d+-\d+', li.xpath('.//p/text()').extract_first())[0]
    #链接
item['link'] = li.xpath('.//h2/a/@href').extract_first()
    yield item
  • 管道(pipelines)

在管道中,主要实现数据的保存,在自定义类中,实现process_item方法,参数有item,spider

如果爬虫组件中,传出了item,那么就需要将item走过这个process_item方法

#数据库的连接
conn = pymongo.MongoClient('localhost',27017)
db = conn.Longtan
table = db.ss
#自定义类
class TextPipline:
#实现process_item方法,item就是爬虫组件中传来的item
    def process_item(self,item,spider):
        table.insert_one(dict(item))
        return item

12.7 保存数据

保存到json

scrapy crawl 爬虫名 -o blog_data.json

另外,也可以每一个Item输出一行JSON,输出后缀改为jl,命令:scrapy crawl 爬虫名 -o blog_data.jl

此外,输出还支持csv、xml、pickle、marshal,还支持了远程ftp、s3等输出

注意,ftp输出需要正确配置用户名、密码、地址、输出路径,否则会报错

12.8 管道(Item Pipline)的使用

注意,pipline是需要被注册的

1. 在settings中,解封或者重新在底下写上

ITEM_PIPELINES = {
   'test01.pipelines.TextPipline': 300,
	#项目名.pipelines.自定义的管道名:
}
# 300:代表着优先级,数字越小,代表着优先级越高

Item生成后,它会自动被送到管道进行处理,我们常用管道来实现以下:

  • 清理HTML数据
  • 验证爬取数据,检查爬取字段
  • 查重并丢弃重复内容
  • 将爬取结果保存到数据库中
class Test01Pipeline:
    def process_item(self, item, spider):
        return item

12.9 整体流程

  1. 需要在items中,定义要爬取的字段
  2. 在爬虫组件中,定义要爬取的url和解析规则
  3. settings中,配置相关的参数
  4. pipeline中,自定义类,实现process_item方法
    在爬虫组件生成的item,通过了yield传递到process_item方法中
  5. 将数据保存在MongoDB、mysql等数据库中

13.MongoDB数据库

13.1 数据库操作

  1. 创建数据库并切换至该数据库下
    use 库名
  2. 查看当前数据库
    db
  3. 查看所有数据库,只能显示非空的数据库
    show dbs
  4. 插入数据
    db.表名.insert({'字段':'值})
  5. 查询所有数据
    db.表名.find()
  6. 查看所有表
    show tables
  7. 删除表
    db.表名.drop()
  8. 删除库
    db.表名.dropDatabase()

13.2 增加数据

字段多少是单独的那条数据,不一致也没有关系

例如:

{‘name’:‘玛卡巴卡’}—{‘name’:‘海绵宝宝’,‘age’:200}

  1. db.表名.insertOne({'字段名':'值})
  2. db.表名.insert({'字段':'值})
  3. db.表名.insertMany([{'字段':'值},{'字段':'值}])

13.3 查询数据

  • 简单查询
    db.表名.find()
  • 分页,返回2条查询数据
    db.表名.find({}).limit(2)
  • 排序,***1***是升序,***-1***是降序
    db.表名.find().sort({'price':1})

等值查询

  • 等值查询(按照条件查询)
    db.表名.find({'price':4.5})

  • 非等值查询(范围查询)

  • ({'price':{'$lt':500,'$gt':100}})#小于500,大于100

  • ***或***查询
    db.表名.find({$or:[{'price':{'$lt':5.5}},{'price':{$gt:500}} ]})

  • >   == $gt  ({'price':{$gt:100}})#价格大于100的
    <   == $lt  ({'price':{$lt:100}})#价格小于100的
    <=  == $lte ...
    >=  == $gte ...
    !=  == $ne  ...
    

模糊查询

  • db.表名.find({'字段名':{$regex:'正则表达式'}})

13.4 修改数据

db.表名.update({'price':2.5},{$set:{'price':500}})

13.5 删除数据

db.表名.remove({'name':'玛卡巴卡'})


pyquery

em`方法

在爬虫组件生成的item,通过了yield传递到process_item方法中
5. 将数据保存在MongoDB、mysql等数据库中

13.MongoDB数据库

13.1 数据库操作

  1. 创建数据库并切换至该数据库下
    use 库名
  2. 查看当前数据库
    db
  3. 查看所有数据库,只能显示非空的数据库
    show dbs
  4. 插入数据
    db.表名.insert({'字段':'值})
  5. 查询所有数据
    db.表名.find()
  6. 查看所有表
    show tables
  7. 删除表
    db.表名.drop()
  8. 删除库
    db.表名.dropDatabase()

13.2 增加数据

字段多少是单独的那条数据,不一致也没有关系

例如:

{‘name’:‘玛卡巴卡’}—{‘name’:‘海绵宝宝’,‘age’:200}

  1. db.表名.insertOne({'字段名':'值})
  2. db.表名.insert({'字段':'值})
  3. db.表名.insertMany([{'字段':'值},{'字段':'值}])

13.3 查询数据

  • 简单查询
    db.表名.find()
  • 分页,返回2条查询数据
    db.表名.find({}).limit(2)
  • 排序,***1***是升序,***-1***是降序
    db.表名.find().sort({'price':1})

等值查询

  • 等值查询(按照条件查询)
    db.表名.find({'price':4.5})

  • 非等值查询(范围查询)

  • ({'price':{'$lt':500,'$gt':100}})#小于500,大于100

  • ***或***查询
    db.表名.find({$or:[{'price':{'$lt':5.5}},{'price':{$gt:500}} ]})

  • >   == $gt  ({'price':{$gt:100}})#价格大于100的
    <   == $lt  ({'price':{$lt:100}})#价格小于100的
    <=  == $lte ...
    >=  == $gte ...
    !=  == $ne  ...
    

模糊查询

  • db.表名.find({'字段名':{$regex:'正则表达式'}})

13.4 修改数据

db.表名.update({'price':2.5},{$set:{'price':500}})

13.5 删除数据

db.表名.remove({'name':'玛卡巴卡'})


pyquery

深度准确性高但是复杂度较高

你可能感兴趣的:(笔记,爬虫)