P6笔记
公开课
虚拟环境就是一个隔离的python环境,不同的项目应该使用不同的虚拟环境(可以使用同一个虚拟环境)
虚拟环境不会导致环境之间的污染
virtualenvwrapper
安装
pip install virtualenvwrapper-win
virtualenvwrapper
的使用
查看所有虚拟环境:lsvirtualenv
创建虚拟环境:mkvirtualenv 环境名
激活虚拟环境:workon 环境名
查看当前虚拟环境下的模块:
退出虚拟环境:deactivate
虚拟环境中安装模块:pycharm
中,选中虚拟环境,然后添加模块,也可以通过pip install
安装
删除虚拟环境:rmvirtualenv 环境名
pycharm
如果没有直接显示虚拟环境,则
settings中选解释器处show all
,加号添加,创建的虚拟环境在
C:\Users\22742\Envs\
目录下,选中Scripts下的python.exe
保证开发与生产环境一致,需要将模块等同一致
在开发机的虚拟环境中,运行命令:
生成模块和其版本pip freeze > requirements.txt
将
requirements
中生成的模块版本进行安装
pip listall -r ./requirements.txt
pip show 包名
pyinstall -F XXX.py
爬虫又称网页蜘蛛或者网页机器人
模拟人操作客户端,向服务器发起网络请求,抓取数据的自动化程序和脚本
通用爬虫是通过抓取数据实现检索服务
爬虫分为聚焦爬虫和通用爬虫
自动化,数据量较小时可以人工获取数据,但往往在公司中爬取的量都在百万级千万级,所以要程序自动化获取数据
B/S架构:
Browser
/Server
,类似淘宝,没有第三方中转,客户端和服务器直接交互C/S:
Client
/server
,类似微信,将微信后端作为中转站,和其他人对话时,需要在中转站传话
pyinstaller
pyinstaller
可以将python文件编译成一个程序,类似go语言的编译
百度,360,搜狐等搜索引擎
原理:
通用爬虫抓取新网站的方式
url
DNS
服务商合作,抓取新网站检索排名:
PageRank
值,访问量、点击量 (SEO
)robots.txt
:如果不想让百度爬取,可以编写robots.txt
,这个协议只是口头上的协议,自己写的爬虫程序不需要遵从
根据特定的需求,抓取指定的数据
思路:
代替浏览器上网
url
,发起请求,获取响应- 解析内容,提取数据
- 将数据存储到本地,数据持久化
#导包
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
中
应用层:
https/http/ftp
http协议:明文传输,端口80
https协议:加密传输,端口443
表示层
会话层
传输层:UDP/TCP
网络层:IP
数据链路层:ARP
物理层:以太网协议
应用层:https/http/ftp/ssh/Sftp/
传输层:UDP/TCP
网络层:IP
数据链路层:ARP
物理层:以太网协议
TCP协议是一种面向连接的,可靠的,基于字节流的传输通信协议
UDP
协议是用户数据报协议,面向无连接的传输层协议,传输相对于TCP
来说,不可靠
通过IP
获取目标计算机的mac地址的协议
交换机不能识别IP地址
远程登录会话
ftp:21
ssh:22
mySQL:3306
MongoDB:27017
Redis:6379
http
与HTTPS
协议的区别https
协议需要到ca申请证书,因而需要一定费用,现阶段国内各大厂商也提供免费的证书http
是超文本传输协议,信息是铭文传输,https
则是具有安全性的ssl
加密传输协议http
和https
使用的是完全不同的连接方式,端口号也不一样,前者是80,后者是443http
的连接很简单,是无状态的,https
协议是由ssl+http
协议构建的可进行加密传输,身份认证的网络协议,比http
协议安全(尽管HTTPS
安全,但是传输的效率没有http
高)url:请求的网址,即统一资源定位符,它可以唯一确定我门向请求的资源
客户端,通常指(web浏览器或
APP
)向服务器发起请求,服务器接收到的请求进行处理,并向客户端发起响应
请求由客户端向服务器发出的,可以分为四部分:请求方法(
Request Method
),请求网址(Request.URL
),请求头(Request Headers
) 请求体(Request Body
)
常见的有八种
GET:请求页面,并返回内容
POST:用于提交表单数据或者文件等,数据包含在请求体中
PUT:从客户端向服务器传送的数据取代指定文档中的内容
DELETE:请求服务器删除指定的页面
HEAD:类似于GET请求,只不过返回的响应中没有具体的内容,用于获取头
CONNECT:把服务器当跳板,让服务器代替客户端访问其他网页
OPTIONS:允许客户端查看服务器的性能
TRACE:会现实服务器收到的请求,主要用于测试或诊断
GET和POST请求的区别
- GET请求中的参数包含在URL里,数据可以在URL中看到,而POST请求的URL一般不会包含这些数据
- GET请求提交的数据最多只有1024字节,而POST方法没有限制
- POST比 GET相对安全
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请求没有请求体,为空
#反爬机制:
为了不让数据泄露,设置了各种阻碍,这就是反爬机制
#反反爬策略
针对网站的反爬机制,采取不同策略
1.脚本:直接忽略
2.scrapy框架:修改配置文件,让爬虫不遵守robots协议
响应是由服务端返回给客户端的,可 以分为三部分:响应状态码,响应头,响应体
响应体,响应的正文数据都在响应体中,比如请求网页时,它的响应体就是网页的HTML代码,我们要爬虫请求网页后,要解析的内容就是响应体
200:成功
301:永久重定向
302:临时重定向
400:错误的请求
401:未授权
403:服务器拒绝此请求
404:未找到
500:服务器内部错误
501:服务器不具备完成请求的功能
502:错误的网关,服务器走位网关或代理,从上游服务器收到无效响应
504:网关超时,服务器作为网关或代理,但是没有及时从上游服务器收到请求
505:HTTP版本不支持
状态码不能完全代表相应状态,部分网站的状态码是自定义的,一切以响应数据为准
res = requests.get(url='https://www.guidaye.com/cp/')
res.text >>>将响应对象转化为str类型
res.json() >>> 将响应对象转化为python中的dict类型,形式(类json)
res.content >>>流形式(数据流,图片就是流形式)
如果响应数据中文乱码,可以用
content.decode('utf-8')
来解决
通用唯一标识符,时间戳,命名空间,随机数,伪随机数来保证生成ID的唯一性
python的
uuid
模块提供UUID
类和函数uuid1()
,uuid3()
,uuid4()
,uuid5()
来生成1, 3, 4, 5各个版本的UUID
( 需要注意的是: python中没有**uuid2()
**这个函数)
uuid1:基于时间戳
uuid3:基于名字的MD5散列值
uuid4:基于随机数,有一定重复概率
uuid5:基于名字的SHA=1散列值
.:任意字符,换行符除外
\d:任意数字
\w:任意数字字母下划线
\s:空白符
#如果是大写的s,w,d,代表'非'
[a-z]:
[A-Z]:
[0-9]:
[^...]
#匹配非其中元素,举例:[^abc]--->匹配除了abc之外的字符
*:匹配0次或多次
+:匹配1次或多次
?:匹配0次或1次 #非贪婪匹配
{m}:m次
{m,}:至少m次
{m,n}:m-n次
{,n}:最多n次
^匹配开始
$匹配结尾
import re
s = ""
res = re.findall(r"href='(.*?)'>",s)
贪婪,尽可能多的匹配
非贪婪,尽可能往少了匹配
re.findall(r'正则表达式','str')
,结果是一个列表,匹配整个字符串
re.search(r'正则表达式','str')
匹配到第一个结果就返回,返回的是一个对象,使用group
取值
re.match(r'正则表达式','str')
从字符串开始进行匹配,返回一个对象,使用group
取值,如果未匹配到,返回None
re.complie
将正则表达式编译为对象,在需要按正则表达式匹配是可以在直接使用该对象调用以上方法
import requests
#打开文件,注意要以rb形式打开
f = open('chn.jpg','rb')
files = {
'file':f
}
res = requests.post(url='***',files = files)
文件也是一种数据,所以,可以通过
files
参数来进行文件的上传
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)
ssl
证书验证https
是http
的安全版本,HTTPS
在http
的基础上多了一个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
证书验证
代理
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)
添加了一个参数,以秒计量timeout=0.1
#添加timeout参数,秒数
import requests
res=requests.get(url=url,timeout=0.1)
给予爬虫与服务器连接的时间限定,设置一个时间,在指定的时间内完成了正常的连接,不报错,如果没有完成,就会报错
requests模块发送请求可以设置超时时间,在超时时间内未得到响应,便会抛出异常
好处:一方面减少了请求的阻塞时间,一方面,可以进行异常处理,执行相应的操作
如果规定时间完成了和服务器连接,之后爬取数据的时间并不算在超时设置的时间内
UA
检测UA
是用户的身份表示,可以表示用户的系统及浏览器信息
在请求过程中,添加headers参数
在同一个关联网页中,为了保存登录状态和各种信息,可以通过cookie来保持
三种方法
- 手动在headers中添加cookie的键值对
cookiejar
对象- 自动封装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)
lxm
库从响应数据中抽取出目标数据的过程,就叫做数据解析
数据解析:
re
,xpath
,BS4
,Pyquery
DOM树与
xpath
解析原理HTML页面标签存在层级关系,即DOM树,在获取目标数据时可以根据网页层级关系定位标签,再获取标签的文本或属性
xpath
解析原理:根据DOM节点的结构关系,进行定位
xpath
基本语法.
:当前节点
/
:根节点
//
:代表任意位置
.//
:从当前节点向下的任意位置匹配
nodename
:节点名定位
nodename[@attribute='value']
:根据节点的属性进行定位
@attribue
:获取节点的属性值,比如获取a标签的href
属性,直接可以/a/@href
text()
:获取节点的文本
//div[@class='asdds']/p/text()
单属性多值匹配:当节点的一个属性有多个值时,根据其中一个进行定位,使用contai ns
函数
'//div[contains(@class,"属性值")]'
多属性匹配:用节点的多个属性共同定位节点and
'//div[@class="asds" and @name="adsadasd"]'
索引定位:[6]
注意,索引从1开始,跟python有区别
位置函数:position
例:/li[position()>2]
last()
函数:定位最后一个,last()-1
代表倒数第二个
加载本地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表达式')
res1 = tree.xpath("//div[@id='007']/text()")
res2 = tree.xpath("//div[@id='007']//text()")
'''
res1展示的是以divid007为根节点的结果,其div下的其他标签不显示
res2展示的是以divid007任意位置的结果,其div下的其他标签内容也一同显示
'''
网页HTML上,有些数据是通过
js
代码填充,所以如果直接使用爬虫,只会爬取到一个标签,并没有其中的元素requests模块和
scrapy
框架在发起请求爬取数据的过程中,不能执行js
代码
selenium是一个web端自动化测试框架,可以通过代码来控制浏览器,比如打开关闭,点击等行为
作用:帮助抓取动态加载的数据,避免反爬
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()
HTML页面嵌套另一个HTML页面
#子页面的跳转
switch_to.frame('id')
#跳转到父级页面
switch_to_default.conent()
注意,selenium默认操作父级页面
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_experimental_option('excludeSwitches',['enable-automation'])
bro = webdriver.Chrome('./chromedriver.exe',options=options)
在爬取数据量大的数据时,耗费时间较长,为了提高效率,可采用多线程爬虫,提高效率,但是,多线程不是原子性,操纵数据可能会导致数据紊乱,不安全
并行:在同一时刻,多个任务同时执行
并发:在同一时间段内,多个任务同时执行
并发时,一般采用了时间片轮转法,即在一个时间段内,给每个程序添加一个时间片,也可以叫做进度条,进度条走完,下一个程序再继续运行,不过时间片轮转时,停顿时间非常的短,所以会造成多个任务同时运行的错误
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)
BS4
什么是无头浏览器(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)
BS4
语法编码流程:
- 导包
from bs4 import BeautifulSoup
- 实例化对象,传两个参数,一个文本,一个解析器,一般为
lxml
suop = BeautifulSoup(res.text,'lxml')
- 选择器解析
#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['***(属性)']:获取节点属性
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())
我们在模拟登录时,时常会遇到一些验证码代码无法准确的识别不同的验证码,这时,就用到了打码平台,它会将图片上的字符或者数字转成字符串返回给你
流程:
- 下载验证码图片
- 传给第三方打码平台
- 进行识别,识别完成之后,传回
ret
- 把
ret
拿回,构造数据
超级鹰平台
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
的异步框架
首先需要安装相应的依赖库
lxml
、wheel
、pywin32
twisted
此依赖安装时,需要从地址下载相应的版本,然后通过pip
来安装
最后安装
scrapy
框架(目前自己使用scrapy
==1.8.0)
创建项目
scrapy startproject 项目名
创建爬虫文件
scrapy genspider 爬虫名 域名
scrapy crawl 爬虫名
└── day01(A)
外层项目目录
└── day01(A)
内层项目目录
└── spiders
放置爬虫的包
└── __init__.py
└── tianting.py
爬虫文件
└── items.py
定义要爬取的数据字段
└── middlewares.py
中间件
└── piplines.py
管道
└── settings.py
配置文件(爬虫配置)
└── scrapy.cfg
配置文件,跟部署相关
五大核心组件
1.引擎(
Scrapy Engine
):
整个框架的调度,负责各个组件之间的通信与数据的传递2.爬虫(
Spiders
):
定义爬取行为和解析规则3.调度器(
Scheduler
):
负责调度所有请求4.下载器(
Downloader
):
负责爬取页面(与互联网交互,爬取页面的)5.管道(
Item Pipeline
):
负责数据持久化
由于引擎负责各个组件之间的调度,所以,所有的组件在相互传递时,都需要经过引擎,比如,爬虫解析后需要将
req
给引擎,然后引擎再给调度器,注意,此时,调度器无法越过引擎去直接调用下载器,所以,需要将req
再次返回给引擎,由引擎来调度下载器和互联网交互
依据请求的生命周期
【调度器】
↑ ↓
2.req 3.req 4.req
【管道】 【引擎】 → 【下载器】 5.req
→ internet
←7.res
←6.res
↑ ↓
1.req 8.res
【爬虫】
9.爬虫经过引擎到达管道
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
需要继承scrapy.Item
类,并且定义类型为scrapy.Field
字段
注意!!items只能够通过字典的方式进行访问和添加
在爬虫组件中使用item容器,需要以下4步:
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
保存到json
中
scrapy crawl 爬虫名 -o blog_data.json
另外,也可以每一个Item输出一行
JSON
,输出后缀改为jl
,命令:scrapy crawl 爬虫名 -o blog_data.jl
此外,输出还支持csv、xml、pickle、marshal
,还支持了远程ftp、s3
等输出
注意,
ftp
输出需要正确配置用户名、密码、地址、输出路径,否则会报错
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
- 需要在items中,定义要爬取的字段
- 在爬虫组件中,定义要爬取的
url
和解析规则- 在
settings
中,配置相关的参数- 在
pipeline
中,自定义类,实现process_item
方法
在爬虫组件生成的item,通过了yield
传递到process_item
方法中- 将数据保存在
MongoDB、mysql
等数据库中
MongoDB
数据库
- 创建数据库并切换至该数据库下
use 库名
- 查看当前数据库
db
- 查看所有数据库,只能显示非空的数据库
show dbs
- 插入数据
db.表名.insert({'字段':'值})
- 查询所有数据
db.表名.find()
- 查看所有表
show tables
- 删除表
db.表名.drop()
- 删除库
db.表名.dropDatabase()
字段多少是单独的那条数据,不一致也没有关系
例如:
{‘name’:‘玛卡巴卡’}—{‘name’:‘海绵宝宝’,‘age’:200}
db.表名.insertOne({'字段名':'值})
db.表名.insert({'字段':'值})
db.表名.insertMany([{'字段':'值},{'字段':'值}])
db.表名.find()
db.表名.find({}).limit(2)
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:'正则表达式'}})
db.表名.update({'price':2.5},{$set:{'price':500}})
db.表名.remove({'name':'玛卡巴卡'})
pyquery
em`方法
在爬虫组件生成的item,通过了
yield
传递到process_item
方法中
5. 将数据保存在MongoDB、mysql
等数据库中
MongoDB
数据库
- 创建数据库并切换至该数据库下
use 库名
- 查看当前数据库
db
- 查看所有数据库,只能显示非空的数据库
show dbs
- 插入数据
db.表名.insert({'字段':'值})
- 查询所有数据
db.表名.find()
- 查看所有表
show tables
- 删除表
db.表名.drop()
- 删除库
db.表名.dropDatabase()
字段多少是单独的那条数据,不一致也没有关系
例如:
{‘name’:‘玛卡巴卡’}—{‘name’:‘海绵宝宝’,‘age’:200}
db.表名.insertOne({'字段名':'值})
db.表名.insert({'字段':'值})
db.表名.insertMany([{'字段':'值},{'字段':'值}])
db.表名.find()
db.表名.find({}).limit(2)
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:'正则表达式'}})
db.表名.update({'price':2.5},{$set:{'price':500}})
db.表名.remove({'name':'玛卡巴卡'})
pyquery
深度准确性高但是复杂度较高