爬虫学习

Jupyter环境安装

安装Anaconda(集成环境), 安装成功后可以提供一种基于浏览器的可视化工具 ---Jupyter.html

什么是jupyter notebook:

Jupyter Notebook是以网页的形式打开,能够在网页页面中直接编写代码和运行代码,代码的运行结果也会直接在代码块下显示。如在编程过程当中须要编写说明文档,可在同一个页面中直接编写,便于做及时的说明和解释.python

jupyter notebook 的主要特色:

1, 编程时具备语法高亮, 缩进,tab补全的功能;mysql

2, 可直接经过浏览器运行代码, 同时在代码块下方展现运行结果;linux

3, 对代码编写说明文档或语句时, 支持Markdown语法.web

安装 jupyter notebook :

安装 jupyter notebook 的前提是须要安装了Python(3.3或2.7以上版本)面试

经过安装Anaconda来解决Jupyter Notebook的安装问题,由于Anaconda已经自动为你安装了Jupter Notebook及其余工具,还有python中超过180个科学包及其依赖项.ajax

一般Anaconda 发行版已经自动安装了jupyter notebook, 若没有安装, 能够在Windows的Anaconda prompt / macOS的终端中输入安装命令:redis

conda install jupyter notebook

运行jupyter notebook

默认端口: 8888sql

cmd 命令行中的指令chrome

-- 在本文件路径下输入: jupyter notebook---开启jupyter服务

浏览器会自动开启jupyter, 其中 ' / ' 表示的根目录是文件夹的目录

浏览器默认显示: http://localhost:8888 localhost 指的是本机, 8888 则是端口号.

注意: 开启服务后, 在操做jupyter notebook 时不能关闭终端, 不然就会断开与本地服务器的连接.

指定端口启动:

自定义端口启动jupyter notebook 能够在终端输入如下命令:

jupyter notebook -port port_number

其中 port_number 是自定义端口号, 直接以数字的形式写在命令中.

启动服务器不打开浏览器:

若想要启动jupyter notebook 可是不打算当即进入到主页面, 就无需马上启动浏览器:

jupyter notebook -no-browser

此时,将会在终端显示启动的服务器信息,并在服务器启动以后,显示出打开浏览器页面的连接。当你须要启动浏览器页面时,只须要复制连接,并粘贴在浏览器的地址栏中,轻按回车变转到了你的Jupyter Notebook页面。

快捷键:

1, 向上插入一个cell: a

2, 向下插入一个cell: b

3, 删除cell: x

4, 将code切换成markdown: m

5, 将markdown切换成code: y

6, 运行cell: shift+enter

7, 查看帮助文档: shift+tab

8, 自动提示: tab

9, 在markdown中 # 能够控制字体大小, 可使用HTML标签更改样式颜色; 在code中, 一个源文件内的代码没有上下之分.

 

爬虫

是经过编写程序, 模拟浏览器上网, 而后让其去互联网上爬取数据的过程.

分类:

增量式:

聚焦爬虫:

增量式爬虫:

反爬机制与反反爬策略

反爬:

1, robots.txt协议

2, UA

3, 数据加密

4, 懒加载

5, 代理ip

 

http和https协议:

抓包工具:

requests模块

代码编写流程:

1, 指定url

2, 发起请求

3, 获取响应对象中的数据

4, 持久化存储

import requests
url = 'https://www.sogou.com/'
response_obj = requests.get(url=url)
page_text = response_obj.text
with open('./sougou.html', 'w', encoding='utf-8')as fp:
  fp.write(page_text)

 

案例1:

爬取搜狗浏览器中的词条搜索信息

# import requests
# url = 'https://www.sogou.com/web'
# # 封装参数
# can_shu = input('enter a word:')
# param = {
#     'query': can_shu
# }
# response_obj = requests.get(url=url, params=param)
# page_text = response_obj.content
# fileName = can_shu+'.html'
# with open(fileName, 'wb')as fp:
#     fp.write(page_text)
#     print('over')

 

案例2:

爬取百度翻译结果

# 案例2 爬取百度翻译结果
# 注意: 翻译的结果是局部刷新, 采用ajax异步请求
import requests
# post 请求
url = 'https://fanyi.baidu.com/sug'
can_shu = input('worlds:')
data = {
  'kw': can_shu
}
response_obj = requests.post(url=url, data=data)
# 返回的response对象以json数据类型展现.若以text形式为字符串, 若以content形式为二进制.
print(response_obj.json())

 

案例3:

爬取豆瓣电影的详情数据

from requests
# get 请求
url = 'https://movie.douban.com/j/chart/top_list'
# 动态捕获电影, 设置一个url字典
param = {
  "type": "5",
"interval_id": "100:90",
"action": "",
"start": "0", # 表示从第'0'索引位置开始
"limit": "1" # 表示爬取一个
}
# 返回一个列表
move_data = requests.get(url=url, params=Parma).json()
print(move_data)

 

案例4:

爬取化妆品公司的生产许可证相关信息

# 案例4
# 反扒机制: UA检测 --> 反反爬策略UA假装.
# --- 请求载体的身份标识: User-Agent.请求载体不同, 标识就不同.基于浏览器和爬虫
#     的请求在乎不同, 因此须要假装成某一浏览器请求.
import requests
id_list = []
# post 请求
url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'

}
# 爬取多页
for page in range(1, 3):
data = {
'on': 'true',
'page': str(page),
'pageSize': '15',
'productName': '',
'conditionType': '1',
'applyname': '',
'applysn': ''
}
data_obj = requests.post(url=url, data=data, headers=headers).json()
# 获取各个ID值
for dic in data_obj['list']:
id = dic['ID']
id_list.append(id)
print(id_list)

# 经过ID 获取各个公司的生产许可证相关信息
detail_url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById'
for id in id_list:
detail_data = {
'id':id
}
detail_json = requests.post(url=detail_url, data=detail_data, headers=headers).json()
print(detail_json)

 

案例5:

爬取图片

# 爬取图片
import requests
url = 'http://d.ifengimg.com/w640_q75/p0.ifengimg.com/pmop/2018/0923/D7D76D3B007F024D2D0964DFE3AD909A68232483_size561_w1080_h1920.jpeg'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'

}
# 图片为二进制数据,能够用content
img_data = requests.get(url=url, headers=headers).content
with open('./gua_jie.jpg', 'wb') as fp:
  fp.write(img_data)

 

案例6:

正则解析爬取动态加载的图片

re.M 表示将正则依次作用于每行;

re.S 表示将正则做用于原数据(总体)

# 爬取动态加载的图片
import requests
import re
import urllib
import os
# 拿到指定页码的图片
url = 'https://www.qiushibaike.com/pic/page/%d/?s=5171142'
start_page = int(input('start page'))
end_page = int(input('end page'))
headers = {
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
}
if not os.path.exists('./qiutu'):
  os.mkdir('./qiutu')
for page in range(start_page, end_page):
  new_url = format(url%page)
  page_text = requests.get(url=new_url, headers=headers).text
  # 提取图片的src属性
  img_url_list = re.findall('<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>', page_text, re.S)
  # 根据src属性爬取纯图片
  for img_url in img_url_list:
      img_url = 'http:'+img_url
      imgName = img_url.split('/')[-1]
      imgPath ='qiutu/'+imgName
      urllib.request.urlretrieve(url=img_url, filename=imgPath)
      print('well done!')
print('完成!!!')

 

bs4模块

环境安装:

pip3 install bs4

pip3 install lxml

解析原理:

1, 将即将要进行解析的源码加载到bs对象

2, 调用bs对象中相关的方法或属性进行源码中的相关标签的定位(锁定即将解析的源码)

3, 将定位到的标签之间存在的文本或属性值获取到(源码中的有用数据)

 

案例1:

下载各个章节名称及其下的文本内容

# bs4模块
from bs4 import BeautifulSoup
import requests
url = 'http://www.shicimingju.com/book/rulinwaishi.html'
headers = {
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
}
page_text = requests.get(url=url, headers=headers).text
# 加载要解析的源码   lxml 为解析器
soup = BeautifulSoup(page_text, 'lxml')
# 进行标签订位
a_list = soup.select('.book-mulu>ul>li>a')
fp = open('儒林外史.txt', 'w', encoding='utf-8')
# 获取到标签之间的有用数据, 并经过该数据下载想要所取得的内容
for a in a_list:
  title = a.string
  detail_url ='http://www.shicimingju.com'+a['href']
  detail_page_text = requests.get(url=detail_url, headers=headers).text
   
  soup = BeautifulSoup(detail_page_text, 'lxml')
  content = soup.find('div', class_='chapter_content').text

  fp.write(title+'\n'+content)
  print(title, '下载完毕')
print('well done!!')
fp.close()

 

xpath模块:

环境安装: pip install lxml

xpath 经常使用表达式:

/ 表示一种层级关系

@ 表示属性定位

图中p[1] 表示 p 标签中的第一个 p 标签.

解析原理:

1, 获取页面源码数据

2, 实例化一个etree的对象, 而且将页面源码数据加载到该对象中

3, 调用该对象的xpath方法进行指定标签订位

注意: xpath函数必须结合xpath表达式进行标签订位和内容捕获

案例1:

获取房源信息

# xpath模块爬取二手房信息

import requests
from lxml import etree

url = 'https://xy.58.com/ershoufang/?PGTID=0d000000-0000-00d2-8801-ee7a9683ca0f&ClickID=2/'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
}
# 获取页面源码数据
page_text = requests.get(url=url, headers=headers).text
# 实例化对象
tree = etree.HTML(page_text)
# li_list 的类型为Element 类型的列表对象
# 标签订位及内容捕获
li_list = tree.xpath('//ul[@class="house-list-wrap"]/li')
fp = open('fang.csv', 'w', encoding='utf-8')
for li in li_list:
  # 进行局部解析要加 '.'
  xin_xi = li.xpath('./div[2]/p[1]//text()')[1]
  price = li.xpath('./div[3]//text()')
  price = ''.join(price)
  fp.write(xin_xi+":"+price+"\n")
fp.close()
print('well done!!')

 

案例2:

获取图片有效信息

# 案例2
# 解析图片
import requests
from lxml import etree
import os
import urllib

url = 'http://pic.netbian.com/4kdongman/index_2.html'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'

}
response = requests.get(url=url, headers=headers)
if not os.path.exists('./imgs'):
  os.mkdir('./imgs')
page_text = response.text
tree = etree.HTML(page_text)
print(tree)
li_list = tree.xpath('//div[@class="slist"]/ul/li')
print(li_list)
for li in li_list:
  img_name = li.xpath('./a/b/text()')[0]
  # 处理中文乱码
  img_name.encode('iso-8859-1').decode('gbk')
  img_url = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
  print(img_name)
  img_path = './img'+img_name+'.jpg'
  urllib.request.urlretrieve(url=img_url, filename=img_path)
  print(img_path, 'nice!!')
print('over!!')

 

案例3:

煎蛋网图片下载

当数据加密时(防盗图),须要解密爬取.

案例4:

爬去免费简历模板

# 爬取站长素材的简历模板
import random
import requests
from lxml import etree
headers ={
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'

}
url = 'http://sc.chinaz.com/jianli/free_%d.html'
for page in range(1, 4):
  if page == 1:
      new_url = 'http://sc.chinaz.com/jianli/free.html'
  else:
      new_url = format(url%page)
# 获取源码数据
response = requests.get(url=new_url, headers=headers)
response.encoding = 'utf-8'
page_text = response.text
# 实例化etree对象
tree = etree.HTML(page_text)
# 定位标签
div_list = tree.xpath('//div[@id="container"]/div')
for div in div_list:
  detail_url = div.xpath('./a/@href')[0]
  name = div.xpath('./a/img/@alt')[0]
  detail_page = requests.get(url=detail_url, headers=headers).text
  tree = etree.HTML(detail_page)
  # 锁定下载地址
  down_list = tree.xpath('//div[@class="clearfix mt20 downlist"]/ul/li/a/@href')
  # 随机选取一个下载地址
  down_url = random.choice(down_list)
  # 获取到下载内容的压缩包
  data = requests.get(url= detail_url, headers=headers).content
  fileName = name+'.rar'
  with open(fileName, 'wb') as fp:
      fp.write(data)
      print('下载成功!!')

注意:​

解决方法:
1, 当请求成功后立刻断开该次请求. 为了及时释放请求池资源,
    ---- 'connection': 'close'
  若首次执行任然报错, 那就再次执行代码.
2, 使用代理ip
3, 使用sleep
案例5:

爬取城市名称

# 解析全部的城市名称
import requests
from lxml import etree
headers = {

}
# 明确要爬取数据的url地址
url = 'https://www.aqistudy.cn/historydata/'
# 肯定要爬取的总体数据信息
page_text = requests.get(url=url, headers=headers).text
# 把要爬取的数据生成在etree对象中
tree = etree.HTML(page_text)
# 锁定要爬取信息的具体页面标签, '|'该管道符表示左边成立则执行右边,管道符左右都要空格.
li_list = tree.xpath('//div[@class="bottom"]/ul/li | //div[@class="bottom"]/ul/div[2]/li')
for li in li_list:
  city_name = li.xpath('./a/text()')[0]
  print(city_name)
案例6:

图片懒加载;:

# 图片懒加载(一种反爬机制)
# 首先排除动态加载和url加密, 直接在Elements中找.
# 当图片还未在可视化范围内时, 图片标签是'src2', 加载后为'src',
# src2 是一个伪属性, 在爬取图片时能够直接用'src2'做为标签, 无需考虑是否在可视化范围内.
案例7:

代理ip:

# 请求过于频繁, 有被封ip的风险, 可使用代理ip
# 代理ip的类型必需要和请求url的协议头保持一致
import requests
url = 'https://www.baidu.com/s?cl=3&wd=ip'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
}
# proxies后为代理ip的键值对
page_text = requests.get(url=url, headers=headers, proxies={'https': '103.203.133.250:8080'}).text
with open('./ip.html', 'w', encoding='utf-8')as fp:
  fp.write(page_text)

 

案例8 验证码

借助云打码平台

注册普通用户和开发者用户

登陆:

登陆普通用户

登陆开发者用户

建立一个软件: 个人软件->建立软件

下载示例代码: 在开发者中心下载最新的DLL(PythonHttp示例下载)

在示例代码中录入普通用户名及密码等相关变量值.

再编写爬虫代码

案例9:模拟登录

案例10:

动态数据加载(selenium):

环境安装: pip install selenium

编码流程:

导包:

from selenium import webdriver
from time import sleep
# 须要借助浏览器终端的驱动程序, 建立浏览器对象
bro = webdriver.Chrome(executable_path=r'D:\chromedriver\chromedriver.exe')
# 标明要使用的浏览器
bro.get(url='https://www.baidu.com/')
# 明确搜索框位置
text_input = bro.find_element_by_id('kw')
# 肯定搜索框内要搜索的内容
text_input.send_keys('美少女战士')
# 点击搜索
bro.find_element_by_id('su').click()
sleep(4)
# 获取当前页面的源码数据(包括动态加载的数据)
print(bro.page_source)
# 退出
bro.quit()
# 获取更多的详情数据
from selenium import webdriver
from time import sleep
url = '想要得到数据的页面详细地址'
# 调用驱动程序
bro = webdriver.Chrome(executable_path=r'D:\chromedriver\chromedriver.exe')
bro.get(url)
sleep(3)
# 页面向下滚动的js代码(有些页面数据是在页面向下滚动时才刷新出来的)
bro.execute_script('window.scrollTo(0, document.body.scrollHeight)')
sleep(3)
bro.execute_script('window.scrollTo(0, document.body.scrollHeight)')
sleep(2)
# 拿到全部页面数据
page_text = bro.page_source
with open('./douban.html', 'w',encoding='utf-8')as fp:
  fp.write(page_text)
sleep(2)
bro.quit()

注意: PhantomJS是一个无界面的浏览器 webdriver.PhantomJS​

 

线程池

from multiprocessing .dummy import Pool

能够设置多个线程池, 同时爬取多个任务

 

移动端数据爬取

 

scrapy框架:

Scrapy框架是一个为了爬取网页数据, 提取结构性数据而编写的应用框架. 所谓框架就是一个已经继承各类功能(高性能异步下载, 队列, 分布式, 解析, 持久化等)的具备很强通用性的项目模板.

安装

linux: pip3 install scrapy

Windows:

1, pip3 install wheel  (安装wheel,为后面安装twisted作准备)

2, 下载twisted  http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 

  注意:原生的scrapy 是不能实现并发功能的 而twisted框架可以于处理并发相关的操做,

  因此scrapy框架是借用twisted中的处理并发功能。

3, 进入下载目录, 执行 pip3 install Twisted- 17.1 - 0 -cp36-cp36m-win_amd64.whl

  是在twisted下载完成后,在下载目录下,由终端(cmd)安装所下载的twisted文件

4, pip3 install pywin32

5, pip3 install scrapy

基础使用

1, 建立项目:

scrapy startproject 项目名

项目目录:

spiders目录(存放爬虫文件) 的做用: url的指定; 请求的发送; 进行数据爬取解析; item管道的提交

items 文件: 只要涉及持久化存储的相关的操做, 必需要写在管道文件。与管道文件结合使用。

pipelines文件(管道文件): 须要接受爬虫文件提交过来的数据, 并对数据进行持久化存储

middlewares文件:中间件

settings文件:配置文件

2, 建立爬虫文件:

先从cmd命令框内进入项目内 再输入 scrapy genspider 爬虫文件名 爬取的起始url

如:

执行爬虫文件: 在终端中输入: scrapy crawl 爬虫文件名 (在终端执行会显示日志信息,若不输出日志文件, 在后面追加 '--nolog' )

当ROBOTS反爬协议生效时, 不能输出response结果, 此时能够更改设置文件中的配置:

  ROBOTSTXT = Flase

当UA反爬机制生效时也不能输出结果, 那么就须要更改设置, 甚至替换请求载体()的身份标识:

  

# -*- coding: utf-8 -*-
import scrapy

# 该文件做用是进行数据的爬取和解析
class FirstSpider(scrapy.Spider):
  # 爬虫文件的名称: 根据爬虫名称能够定位到指定的爬虫文件
  name = 'first'
  # 容许的域名
  allowed_domains = ['www.baidu.com']
  # 起始URL列表(要爬取的url地址必须在容许的域名下,为了避免冲突也能够注释掉域名)
  start_urls = ['https://www.baidu.com/',...]

  # 用于解析: response就是起始URL对应的响应对象,每执行一次起始url列表中的url,就会调用一次该方法.
  def parse(self, response):
      print(response)
3. 基于终端指令的持久化存储
  • 保证爬虫文件的parse方法中有可迭代类型对象(一般为列表or字典)的返回,该返回值能够经过终端指令的形式写入指定格式的文件中进行持久化操做。

执行输出指定格式进行存储:将爬取到的数据写入不一样格式的文件中进行存储
  scrapy crawl 爬虫名称 -o xxx.json
  scrapy crawl 爬虫名称 -o xxx.xml
  scrapy crawl 爬虫名称 -o xxx.csv

 

# -*- coding: utf-8 -*-
import scrapy

# 该文件做用是进行数据的爬取和解析
class FirstSpider(scrapy.Spider):
  # 爬虫文件的名称: 根据爬虫名称能够定位到指定的爬虫文件
  name = 'first'
  # 容许的域名
  # allowed_domains = ['www.baidu.com']
  # 起始URL列表(要爬取的url地址必须在容许的域名下,为了避免冲突也能够注释掉域名)
  start_urls = ['https://www.qiushibaike.com/text/']

  # 用于解析: response就是起始URL对应的对象,每执行一次起始url列表中的url
  # 就会调用一次该方法.
  def parse(self, response):
      all_data = []
      # xpath 返回的列表元素类型是Select类型 (scrapy框架可以调用xpath进行高性能的解析)
      div_list = response.xpath('//div[@id="content-left"]/div')
      for div in div_list:
        #extract 可以接收select对象中的参数值
          title = div.xpath('./div[1]/a[2]/h2/text() | ./div/span[2]/h2/text()')[0].extract()
          # 若是能保证列表内只有一个元素, 能够用extract_first
          # title = div.xpath('./div[1]/a[2]/h2/text() | ./div/span[2]/h2/text()').extract_first()
          print(title)
          content = div.xpath('./a/div/span/text()').extract()
          # 该种状况不能往数据库中存储
          dic = {
              'title': title,
              'content': content
          }
       # 把每个字典中的键值都存在列表中。
          all_data.append(dic)
  # 基于终端指令的持久化存储: 能够经过终端指令的形式将parse方法的返回值中存储的数据进行本地磁盘的持久化存储.
      return all_data

 基于终端指令的存储命令: scrapy crawl 爬虫文件名 -o 文件名.后缀

4.基于管道的持久化存储

scrapy框架中已经为咱们专门集成好了高效、便捷的持久化操做功能,咱们直接使用便可。要想使用scrapy的持久化操做功能,咱们首先来认识以下两个文件:

    items.py:数据结构模板文件。定义数据属性。
  pipelines.py:管道文件。接收数据(items),进行持久化操做。

持久化流程:
  1.获取解析到的数据值
  2.将解析的数据封装到item对象中(item类中进行相关属性的声明)。
  3.使用yield关键字将item对象提交到管道进行持久化操做。
  4.在管道文件中的process_item方法中接收爬虫文件提交过来的item对象,而后编写持久化存储的代码将item对象中存储的数据进行持久化存储
  5.settings.py配置文件中开启管道

将糗事百科首页中的段子和做者数据爬取下来,而后进行持久化存储

- 爬虫文件:qiubaiDemo.py

# -*- coding: utf-8 -*-
import scrapy
from secondblood.items import SecondbloodItem

class QiubaidemoSpider(scrapy.Spider):
   name = 'qiubaiDemo'
   allowed_domains = ['www.qiushibaike.com']
   start_urls = ['http://www.qiushibaike.com/']

   def parse(self, response):
       odiv = response.xpath('//div[@id="content-left"]/div')
       for div in odiv:
           # xpath函数返回的为列表,列表中存放的数据为Selector类型的数据。咱们解析到的内容被封装在了Selector对象中,须要调用extract()函数将解析的内容从Selecor中取出。
           author = div.xpath('.//div[@class="author clearfix"]//h2/text()').extract_first()
           author = author.strip('\n')#过滤空行
           content = div.xpath('.//div[@class="content"]/span/text()').extract_first()
           content = content.strip('\n')#过滤空行

           #将解析到的数据封装至items对象中
       # 实例化一个item类型的对象
           item = SecondbloodItem()
       # ‘’号中的author是item文件中声明的属性名称
           item['author'] = author
           item['content'] = content

           yield item #提交item对象到管道文件(pipelines.py)

- items文件:items.py

import scrapy


class SecondbloodItem(scrapy.Item):
   # define the fields for your item here like:
   # name = scrapy.Field()
   author = scrapy.Field() #存储做者(实例化须要存储的对象)
   content = scrapy.Field() #存储段子内容

- 管道文件:pipelines.py

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

​# 管道文件:须要接受爬虫文件提交过来的数据,并对数据进行持久化存储(IO操做)
class SecondbloodPipeline(object):
   #构造方法
   def __init__(self):
       self.fp = None  #定义一个文件描述符属性
  #下列都是在重写父类的方法:
   #开始爬虫时,执行一次
   def open_spider(self,spider):
       print('爬虫开始')
       self.fp = open('./data.txt', 'w')

   #由于该方法会被执行调用屡次,因此文件的开启和关闭操做写在了另外两个只会各自执行一次的方法中。
   def process_item(self, item, spider):
       #将爬虫程序提交的item进行持久化存储
       self.fp.write(item['author'] + ':' + item['content'] + '\n')
       return item

   #结束爬虫时,执行一次
   def close_spider(self,spider):
       self.fp.close()
       print('爬虫结束')

  注意:默认管道机制并无开启,须要在配置文件中手动开启。

- 配置文件:settings.py

#开启管道
ITEM_PIPELINES = {
   'secondblood.pipelines.SecondbloodPipeline': 300, #300表示为优先级,值越小优先级越高
}
5. 基于mysql的管道存储

在管道文件里将item对象中的数据值存储到了磁盘中,若是将item数据写入mysql数据库的话,只须要将上述案例中的管道文件修改为以下形式:

- pipelines.py文件

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

#导入数据库的类
import pymysql
class QiubaiproPipelineByMysql(object):

   conn = None  #mysql的链接对象声明
   cursor = None#mysql游标对象声明
   def open_spider(self,spider):
       print('开始爬虫')
       #连接数据库
       self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='123456',db='qiubai')
   #编写向数据库中存储数据的相关代码
   def process_item(self, item, spider):
       #1.连接数据库
       #2.执行sql语句
       sql = 'insert into qiubai values("%s","%s")'%(item['author'],item['content'])
       self.cursor = self.conn.cursor()
       #执行事务
       try:
           self.cursor.execute(sql)
           self.conn.commit()
       except Exception as e:
           print(e)
           self.conn.rollback()

       return item
   def close_spider(self,spider):
       print('爬虫结束')
       self.cursor.close()
       self.conn.close()

settings.py

ITEM_PIPELINES = {
   'qiubaiPro.pipelines.QiubaiproPipelineByMysql': 300,
}

 

6. 基于redis的管道存储

在管道文件里将item对象中的数据值存储到了磁盘中,若是将item数据写入redis数据库的话,只须要将上述案例中的管道文件修改为以下形式:

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

import redis

class QiubaiproPipelineByRedis(object):
   conn = None
   def open_spider(self,spider):
       print('开始爬虫')
       #建立连接对象
       self.conn = redis.Redis(host='127.0.0.1',port=6379)
   def process_item(self, item, spider):
       dict = {
           'author':item['author'],
           'content':item['content']
      }
       #写入redis中
       self.conn.lpush('data', dict)
       return item

- pipelines.py文件

ITEM_PIPELINES = {
   'qiubaiPro.pipelines.QiubaiproPipelineByRedis': 300,
}

面试题:若是最终须要将爬取到的数据值一份存储到磁盘文件,一份存储到数据库中,则应该如何操做scrapy?  

- 答:管道文件中的代码为

#该类为管道类,该类中的process_item方法是用来实现持久化存储操做的。
class DoublekillPipeline(object):

   def process_item(self, item, spider):
       #持久化操做代码 (方式1:写入磁盘文件)
       return item

#若是想实现另外一种形式的持久化操做,则能够再定制一个管道类:
class DoublekillPipeline_db(object):

   def process_item(self, item, spider):
       #持久化操做代码 (方式1:写入数据库)
       return item

在settings.py开启管道操做代码为:

#下列结构为字典,字典中的键值表示的是即将被启用执行的管道文件和其执行的优先级。
ITEM_PIPELINES = {
  'doublekill.pipelines.DoublekillPipeline': 300,
   'doublekill.pipelines.DoublekillPipeline_db': 200,
}

#上述代码中,字典中的两组键值分别表示会执行管道文件中对应的两个管道类中的process_item方法,实现两种不一样形式的持久化操做。

 

7. 递归爬取解析多页页面数据

- 需求:将糗事百科全部页码的做者和段子内容数据进行爬取切持久化存储

- 需求分析:每个页面对应一个url,则scrapy工程须要对每个页码对应的url依次发起请求,而后经过对应的解析方法进行做者和段子内容的解析。

实现方案:

1.将每个页码对应的url存放到爬虫文件的起始url列表(start_urls)中。(不推荐)

2.使用Request方法手动发起请求。(推荐)

代码展现:

# -*- coding: utf-8 -*-
import scrapy
from qiushibaike.items import QiushibaikeItem
# scrapy.http import Request
class QiushiSpider(scrapy.Spider):
   name = 'qiushi'
   allowed_domains = ['www.qiushibaike.com']
   start_urls = ['https://www.qiushibaike.com/text/']

   #爬取多页
   pageNum = 1 #起始页码
   url = 'https://www.qiushibaike.com/text/page/%s/' #每页的url

   def parse(self, response):
       div_list=response.xpath('//*[@id="content-left"]/div')
       for div in div_list:
           #//*[@id="qiushi_tag_120996995"]/div[1]/a[2]/h2
           author=div.xpath('.//div[@class="author clearfix"]//h2/text()').extract_first()
           author=author.strip('\n')
           content=div.xpath('.//div[@class="content"]/span/text()').extract_first()
           content=content.strip('\n')
           item=QiushibaikeItem()
           item['author']=author
           item['content']=content

           yield item #提交item到管道进行持久化

        #爬取全部页码数据
       if self.pageNum <= 13: #一共爬取13页(共13页)
           self.pageNum += 1
           url = format(self.url % self.pageNum)

           #递归爬取数据:callback表示指定的解析方法。递归调用parse函数
(将url请求后,获得的相应数据继续进行parse解析)。
            yield scrapy.Request(url=url,callback=self.parse)

8. 五大核心组件工做流程:  

  • 引擎(Scrapy Engine) 用来处理整个系统的数据流处理, 触发事务(框架核心)

  • 调度器(Scheduler) 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 能够想像成一个URL(抓取网页的网址或者说是连接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址

  • 下载器(Downloader) 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是创建在twisted这个高效的异步模型上的)

  • 爬虫(Spiders) 爬虫是主要干活的, 用于从特定的网页中提取本身须要的信息, 即所谓的实体(Item)。用户也能够从中提取出连接,让Scrapy继续抓取下一个页面

  • 项目管道(Pipeline) 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证明体的有效性、清除不须要的信息。当页面被爬虫解析后,将被发送到项目管道,并通过几个特定的次序处理数据。

  数据爬取流程:spiders文件拥有爬取的url地址,并封装成请求对象,传递给引擎,引擎接着传递给调度器,调度器会把传递来的请求对象中的请求队列内的每一条请求,经过中间件传递给下载器,进行数据下载,并把数据封装到响应对象中,而后再传递给引擎,引擎接着把响应对象传递给spiders文件,进行解析。若爬取的是动态加载的数据,则须要把下载器经过中间件传递给引擎的响应对象拦截,在中间件中从新实例化一个响应对象,该实例化对象中包含动态的页面数据。

简单反爬手段:

  UA假装 :

    UA即 User-Agent 表示用户代理,其实就是用户身份。当浏览器随便访问一个网站时,经过请求的Headers中查看Request Headers的User-Agent,能够看到后面有浏览器的名字,这就表明用户身份是浏览器。若直接以爬虫的身份访问,就会受到很大限制,那么经过修改UA假装成浏览器,就是UA假装了。

9. post请求发送

- 问题:在以前代码中,咱们历来没有手动的对start_urls列表中存储的起始url进行过请求的发送,可是起始url的确是进行了请求的发送,那这是如何实现的呢?

- 解答:实际上是由于爬虫文件中的爬虫类继承到了Spider父类中的start_requests(self)这个方法,该方法就能够对start_urls列表中的url发起请求:

  def start_requests(self):
       for u in self.start_urls:
          yield scrapy.Request(url=u,callback=self.parse)

【注意】该方法默认的实现,是对起始的url发起get请求,若是想发起post请求,则须要子类重写该方法。

  -方法: 重写start_requests方法,让其发起post请求:

def start_requests(self):
       #请求的url
       post_url = 'http://fanyi.baidu.com/sug'
       # post请求参数
       formdata = {
           'kw': 'wolf',
      }
       # 发送post请求
       yield scrapy.FormRequest(url=post_url, formdata=formdata, callback=self.parse)

 

10. Scrapy的日志等级

  - 在使用scrapy crawl spiderFileName运行程序时,在终端里打印输出的就是scrapy的日志信息。

  - 日志信息的种类:

        ERROR : 通常错误

        WARNING : 警告

        INFO : 通常的信息

        DEBUG : 调试信息

       

  - 设置日志信息指定输出:

    在settings.py配置文件中,加入

LOG_LEVEL = ‘指定日志信息种类’便可。

LOG_FILE = 'log.txt'则表示将日志信息写入到指定文件中进行存储。

11. 请求传参

  - 在某些状况下,咱们爬取的数据不在同一个页面中,例如,咱们爬取一个电影网站,电影的名称,评分在一级页面,而要爬取的其余电影详情在其二级子页面中。这时咱们就须要用到请求传参。

  - 案例展现:爬取www.id97.com电影网,将一级页面中的电影名称,类型,评分一级二级页面中的上映时间,导演,片长进行爬取。

  爬虫文件:

# -*- coding: utf-8 -*-
import scrapy
from moviePro.items import MovieproItem

class MovieSpider(scrapy.Spider):
   name = 'movie'
   allowed_domains = ['www.id97.com']
   start_urls = ['http://www.id97.com/']

   def parse(self, response):
       div_list = response.xpath('//div[@class="col-xs-1-5 movie-item"]')

       for div in div_list:
           item = MovieproItem()
           item['name'] = div.xpath('.//h1/a/text()').extract_first()
           item['score'] = div.xpath('.//h1/em/text()').extract_first()
           #xpath(string(.))表示提取当前节点下全部子节点中的数据值(.)表示当前节点
           item['kind'] = div.xpath('.//div[@class="otherinfo"]').xpath('string(.)').extract_first()
           item['detail_url'] = div.xpath('./div/a/@href').extract_first()
           #请求二级详情页面,解析二级页面中的相应内容,经过meta参数进行Request的数据传递
           yield scrapy.Request(url=item['detail_url'],callback=self.parse_detail,meta={'item':item})

   def parse_detail(self,response):
       #经过response获取item
       item = response.meta['item']
       item['actor'] = response.xpath('//div[@class="row"]//table/tr[1]/a/text()').extract_first()
       item['time'] = response.xpath('//div[@class="row"]//table/tr[7]/td[2]/text()').extract_first()
       item['long'] = response.xpath('//div[@class="row"]//table/tr[8]/td[2]/text()').extract_first()
       #提交item到管道
       yield item

 items文件:

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class MovieproItem(scrapy.Item):
   # define the fields for your item here like:
   name = scrapy.Field()
   score = scrapy.Field()
   time = scrapy.Field()
   long = scrapy.Field()
   actor = scrapy.Field()
   kind = scrapy.Field()
   detail_url = scrapy.Field()

管道文件:

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

import json
class MovieproPipeline(object):
   def __init__(self):
       self.fp = open('data.txt','w')
   def process_item(self, item, spider):
       dic = dict(item)
       print(dic)
       json.dump(dic,self.fp,ensure_ascii=False)
       return item
   def close_spider(self,spider):
       self.fp.close()

12. 如何提升scrapy的爬取效率
增长并发:
  默认scrapy开启的并发线程为32个,能够适当进行增长。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。

下降日志级别:
  在运行scrapy时,会有大量日志信息的输出,为了减小CPU的使用率。能够设置log输出信息为INFO或者ERROR便可。在配置文件中编写:LOG_LEVEL = ‘INFO’

禁止cookie:
  若是不是真的须要cookie,则在scrapy爬取数据时能够进制cookie从而减小CPU的使用率,提高爬取效率。在配置文件中编写:COOKIES_ENABLED = False

禁止重试:
  对失败的HTTP进行从新请求(重试)会减慢爬取速度,所以能够禁止重试。在配置文件中编写:RETRY_ENABLED = False

减小下载超时:
  若是对一个很是慢的连接进行爬取,减小下载超时能够能让卡住的连接快速被放弃,从而提高效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s

测试案例:爬取校花网校花图片 www.521609.com

# -*- coding: utf-8 -*-
import scrapy
from xiaohua.items import XiaohuaItem

class XiahuaSpider(scrapy.Spider):

   name = 'xiaohua'
   allowed_domains = ['www.521609.com']
   start_urls = ['http://www.521609.com/daxuemeinv/']

   pageNum = 1
   url = 'http://www.521609.com/daxuemeinv/list8%d.html'

   def parse(self, response):
       li_list = response.xpath('//div[@class="index_img list_center"]/ul/li')
       for li in li_list:
           school = li.xpath('./a/img/@alt').extract_first()
           img_url = li.xpath('./a/img/@src').extract_first()

           item = XiaohuaItem()
           item['school'] = school
           item['img_url'] = 'http://www.521609.com' + img_url

           yield item

       if self.pageNum < 10:
           self.pageNum += 1
           url = format(self.url % self.pageNum)
           #print(url)
           yield scrapy.Request(url=url,callback=self.parse)

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class XiaohuaItem(scrapy.Item):
   # define the fields for your item here like:
   # name = scrapy.Field()
   school=scrapy.Field()
   img_url=scrapy.Field()

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html

import json
import os
import urllib.request
class XiaohuaPipeline(object):
   def __init__(self):
       self.fp = None

   def open_spider(self,spider):
       print('开始爬虫')
       self.fp = open('./xiaohua.txt','w')

   def download_img(self,item):
       url = item['img_url']
       fileName = item['school']+'.jpg'
       if not os.path.exists('./xiaohualib'):
           os.mkdir('./xiaohualib')
       filepath = os.path.join('./xiaohualib',fileName)
       urllib.request.urlretrieve(url,filepath)
       print(fileName+"下载成功")

   def process_item(self, item, spider):
       obj = dict(item)
       json_str = json.dumps(obj,ensure_ascii=False)
       self.fp.write(json_str+'\n')

       #下载图片
       self.download_img(item)
       return item

   def close_spider(self,spider):
       print('结束爬虫')
       self.fp.close()

配置文件:

USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Configure maximum concurrent requests performed by Scrapy (default: 16)
CONCURRENT_REQUESTS = 100
COOKIES_ENABLED = False
LOG_LEVEL = 'ERROR'
RETRY_ENABLED = False
DOWNLOAD_TIMEOUT = 3
# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16
DOWNLOAD_DELAY = 3

 

13. scrapy中的selenium

未完......

相关文章
相关标签/搜索