爬虫 + 数据

爬虫 + 数据 - day01

启动:jupyter notebook
介绍:
anaconda是一个集成环境(数据分析+机器学习)
提供了一个叫做jupyter的可视化工具(基于浏览器)
jupyter的基本使用
快捷键:
插入cell:a,b
删除:x
执行:shift+enter
切换cell的模式:y,m
tab:自动补全
打开帮助文档:shift+tab

1. 什么是爬虫 :

  • 通过编写程序模拟浏览器上网,从互联网中爬取需要的数据的过程

2. 爬虫的分类 :

  • 通用爬虫 : 爬取一整张页面源码数据.搜索引擎 (抓取系统→内部封好的一套爬虫程序) 重点使用的是该种形式爬虫
  • 聚焦爬虫 : 抓取页面中指定的局部数据
  • 增量式爬虫 : 监测网站数据更新的情况.抓取网站最新更新的数据

3. 爬虫安全性的探究

  • 风险所在

    • 爬虫干扰了被访问网站的正常运营;
    • 爬虫抓取了受到法律保护的特定类型的数据或信息
  • 如何规避风险

    • 严格遵守网站设置的robots协议;
    • 在规避反爬虫措施的同时,需要优化自己的代码,避免干扰被访问网站的正常运行;
    • 在使用、传播抓取到的信息时,应审查所抓取的内容,如发现属于用户的个人信息、隐私或者他人的商业秘密的,应及时停止并删除

    爬虫机制 :应用在网站中

    反反爬机制 : 应用在爬虫程序中

    第一个反爬机制 :

    ​ robots协议:纯文本的协议

    • 特点:防君子不防小人

4. http & https

  • 什么是http协议
    • 服务器和客户端进行数据交互的某种形式
  • https - 安全 (数据加密) 的http协议

头部信息

1、通用头部

通用头域包含请求和响应消息都支持的头域。

Request URL:请求的URL地址
Request Method: 请求方法,get/post/put/……
Status Code:状态码,200 为请求成功
Remote Address:路由地址

2、请求头部

1) Accept:  告诉WEB服务器自己接受什么介质类型,*/* 表示任何类型,type/* 表示该类型下的所有子类型;
2)Accept-Charset:  浏览器申明自己接收的字符集
  Accept-Encoding:浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,		deflate)
3)Accept-Language:  浏览器申明自己接收的语言。语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等。
4)Authorization:  当客户端接收到来自WEB服务器的 WWW-Authenticate 响应时,该头部来回应自己的身份验证信息给WEB服务器。
5)Connection:表示是否需要持久连接。close(告诉WEB服务器或者代理服务器,在完成本次请求的响应后,断开连接,
     不要等待本次连接的后续请求了)。keep-alive(告诉WEB服务器或者代理服务器,在完成本次请求的响应后,保持连接,等待本次连接的后续请求)。
6)Referer:发送请求页面URL。浏览器向 WEB 服务器表明自己是从哪个 网页/URL 获得/点击 当前请求中的网址/URL。
7)User-Agent: 浏览器表明自己的身份(是哪种浏览器)。
8)Host: 发送请求页面所在域。
9)Cache-Control:浏览器应遵循的缓存机制。
       no-cache(不要缓存的实体,要求现在从WEB服务器去取)
       max-age:(只接受 Age 值小于 max-age 值,并且没有过期的对象) 
       max-stale:(可以接受过去的对象,但是过期时间必须小于 max-stale 值)  
       min-fresh:(接受其新鲜生命期大于其当前 Age 跟 min-fresh 值之和的缓存对象)
10)Pramga:主要使用 Pramga: no-cache,相当于 Cache-Control: no-cache。
11)Range:浏览器(比如 Flashget 多线程下载时)告诉 WEB 服务器自己想取对象的哪部分。
12)Form:一种请求头标,给定控制用户代理的人工用户的电子邮件地址。
13)Cookie:这是最重要的请求头信息之一

3、响应头部

1)Age:当代理服务器用自己缓存的实体去响应请求时,用该头部表明该实体从产生到现在经过多长时间了。
2)Accept-Ranges:WEB服务器表明自己是否接受获取其某个实体的一部分(比如文件的一部分)的请求。bytes:表示接受,none:表示不接受。
3) Cache-Control:服务器应遵循的缓存机制。
    public(可以用 Cached 内容回应任何用户)
    private(只能用缓存内容回应先前请求该内容的那个用户)
    no-cache(可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端) 
    max-age:(本响应包含的对象的过期时间)  
    ALL:  no-store(不允许缓存)  
4) Connection: 是否需要持久连接
        close(连接已经关闭)。
        keepalive(连接保持着,在等待本次连接的后续请求)。
        Keep-Alive:如果浏览器请求保持连接,则该头部表明希望 WEB 服务器保持连接多长时间(秒)。例如:Keep-						Alive:300
5)Content-Encoding:WEB服务器表明自己使用了什么压缩方法(gzip,deflate)压缩响应中的对象。 例如:Content-Encoding:gzip 
6)Content-Language:WEB 服务器告诉浏览器自己响应的对象的语言。
7)Content-Length:WEB 服务器告诉浏览器自己响应的对象的长度。例如:Content-Length: 26012
8)Content-Range:WEB 服务器表明该响应包含的部分对象为整个对象的哪个部分。例如:Content-Range: bytes 21010-47021/47022
9)Content-Type:WEB 服务器告诉浏览器自己响应的对象的类型。例如:Content-Type:application/xml
10)Expired:WEB服务器表明该实体将在什么时候过期,对于过期了的对象,只有在跟WEB服务器验证了其有效性后,才能用来响应客户请求。
11) Last-Modified:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间等等。
12) Location:WEB 服务器告诉浏览器,试图访问的对象已经被移到别的位置了,到该头部指定的位置去取。
13)Proxy-Authenticate: 代理服务器响应浏览器,要求其提供代理身份验证信息。
14)Server: WEB 服务器表明自己是什么软件及版本等信息。
15)Refresh:表示浏览器应该在多少时间之后刷新文档,以秒计。

https的加密方式

对称密钥加密
非对称密钥加密
证书密钥加密

5. request模块

基于网络请求的python模块

作用 :模拟浏览器发送请求,实现爬虫

环境安装 : pip install request

编码流程 :

  • 指定url
  • 发起请求
  • 获取响应数据
  • 持久化存储

1. 爬取搜狗首页的页面源码数据

import requests
#1.指定url
url = 'https://www.sogou.com/'
#2.请求发送:get返回的是一个响应对象
response = requests.get(url=url)
#3.获取响应数据:text返回的是字符串形式的响应数据
page_text = response.text
#4.持久化存储
with open('./sogou.html','w',encoding='utf-8') as fp:
    fp.write(page_text)

2. 实现一个简易的网页采集器

请求参数的动态化

url = 'https://www.sogou.com/web'
#请求参数的动态化
wd = input('enter a key word:')
params = {
    'query':wd
}
response = requests.get(url=url,params=params)
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
    fp.write(page_text)
print(fileName,'爬取成功!')

上述代码问题:

  • 乱码问题
    • response.encoding = 'xxx'
  • 数据丢失
    • 反爬机制:UA检测
    • 反反爬策略:UA伪装
#乱码问题的解决
url = 'https://www.sogou.com/web'
#请求参数的动态化
wd = input('enter a key word:')
params = {
    'query':wd
}

response = requests.get(url=url,params=params)

#将响应数据的编码格式手动进行指定
response.encoding = 'utf-8'
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
    fp.write(page_text)
print(fileName,'爬取成功!')
#UA伪装操作
url = 'https://www.sogou.com/web'
#请求参数的动态化
wd = input('enter a key word:')
params = {
    'query':wd
}

#UA伪装
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
}
response = requests.get(url=url,params=params,headers=headers)

#将响应数据的编码格式手动进行指定
response.encoding = 'utf-8'
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
    fp.write(page_text)
print(fileName,'爬取成功!')

3. 动态加载的数据

通过另一个网络请求 (ajax) 请求到的数据

爬取豆瓣电影中动态加载出的电影详情数据 :

url = 'https://movie.douban.com/j/chart/top_list'
#参数动态化
params = {
    'type': '17',
    'interval_id': '100:90',
    'action': '',
    'start': '0',
    'limit': '200',
}
response = requests.get(url=url,params=params,headers=headers)
#json()返回的是序列化好的对象
movie_list = response.json()
for movie in movie_list:
    print(movie['title'],movie['score'])

总结:对一个陌生的网站进行数据爬取的时候,首先确定的一点就是爬取的数据是否为动态加载出来的
	是:需要通过抓包工具捕获到动态加载数据对应的数据包,从中提取出url和请求参数。
	不是:直接对浏览器地址栏的url发起请求即可
如何检测爬取的数据是不是动态加载出来的?
	通过抓包工具进行局部搜索就可以验证数据是否为动态加载
		搜索到:不是动态加载
		搜索不到:是动态加载
如何定位动态加载的数据在哪呢?
	通过抓包工具进行全局搜索进行定位

4. 爬取肯德基的餐厅位置信息

http://www.kfc.com.cn/kfccda/storelist/index.aspx

url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
data = {
    'cname': '',
    'pid': '',
    'keyword': '上海',
    'pageIndex': '1',
    'pageSize': '10',
}
address_dic = requests.post(url=url,data=data,headers=headers).json()
for dic in address_dic['Table1']:
    print(dic['addressDetail'])

5. 面试题

- 需求
https://www.fjggfw.gov.cn/Website/JYXXNew.aspx 福建省公共资源交易中心
提取内容:
工程建设中的中标结果信息/中标候选人信息
1. 完整的html中标信息
2. 第一中标候选人
3. 中标金额
4. 中标时间
5. 其它参与投标的公司

- 实现思路
    - 确认爬取的数据都是动态加载出来的
    - 在首页中捕获到ajax请求对应的数据包,从该数据包中提取出请求的url和请求参数
    - 对提取到的url进行请求发送,获取响应数据(json)
    - 从json串中提取到每一个公告对应的id值
    - 将id值和中标信息对应的url进行整合,进行请求发送捕获到每一个公告对应的中标信息数据

post_url = 'https://www.fjggfw.gov.cn/Website/AjaxHandler/BuilderHandler.ashx'
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
    'Cookie': '_qddac=4-3-1.4euvh3.x4wulp.k1hj8mnw; ASP.NET_SessionId=o4xkycpib3ry5rzkvfcamxzk; Hm_lvt_94bfa5b89a33cebfead2f88d38657023=1570520304; __root_domain_v=.fjggfw.gov.cn; _qddaz=QD.89mfu7.7kgq8w.k1hj8mhg; _qdda=4-1.4euvh3; _qddab=4-x4wulp.k1hj8mnw; _qddamta_2852155767=4-0; _qddagsx_02095bad0b=2882f90558bd014d97adf2d81c54875229141367446ccfed2b0c8913707c606ccf30ec99a338fed545821a5ff0476fd6332b8721c380e9dfb75dcc00600350b31d85d17d284bb5d6713a887ee73fa35c32b7350c9909379a8d9f728ac0c902e470cb5894c901c4176ada8a81e2ae1a7348ae5da6ff97dfb43a23c6c46ec8ec10; Hm_lpvt_94bfa5b89a33cebfead2f88d38657023=1570520973'
}
data = {
    'OPtype': 'GetListNew',
    'pageNo': '1',
    'pageSize': '10',
    'proArea': '-1',
    'category': 'GCJS',
    'announcementType': '-1',
    'ProType': '-1',
    'xmlx': '-1',
    'projectName': '',
    'TopTime': '2019-07-10 00:00:00',
    'EndTime': '2019-10-08 23:59:59',
    'rrr': '0.7293828344656237',
}
post_data = requests.post(url=post_url,headers=headers,data=data).json()
for dic in post_data['data']:
    _id = int(dic['M_ID'])
    detail_url = 'https://www.fjggfw.gov.cn/Website/AjaxHandler/BuilderHandler.ashx?OPtype=GetGGInfoPC&ID={}&GGTYPE=5&url=AjaxHandler%2FBuilderHandler.ashx'.format(_id)
    company_data = requests.get(url=detail_url,headers=headers).json()['data']
    company_str = ''.join(company_data)
    print(company_str)
    

6. 数据解析

1. 如何爬取图片数据?

- 基于requests|
- 基于urllib
- 区别:urllib中的urlretrieve不可以进行UA伪装

import requests
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
}

#基于requests的图片爬取
url = 'http://tva1.sinaimg.cn/mw600/007QUzsKgy1g7qzr59hk7j30cs0gxn82.jpg'
img_data = requests.get(url=url,headers=headers).content #content返回的是byte类型的响应数据
with open('./123.jpg','wb') as fp:
    fp.write(img_data)

#基于urllib的图片爬取
from urllib import request
url = 'http://tva1.sinaimg.cn/mw600/007QUzsKgy1g7qzr59hk7j30cs0gxn82.jpg'
request.urlretrieve(url,'./456.jpg')

2. 数据解析

数据解析

  • 概念:将一整张页面中的局部数据进行提取/解析

  • 作用:用来实现聚焦爬虫的吧

  • 实现方式:

    • 正则
    • bs4
    • xpath
    • pyquery
  • 数据解析的通用原理是什么?

    • 标签的定位
    • 数据的提取
  • 页面中的相关的字符串的数据都存储在哪里呢?

    • 标签中间
    • 标签的属性中
  • - 基于聚焦爬虫的编码流程
        - 指定url
        - 发起请求
        - 获取响应数据
        - 数据解析
        - 持久化存储
    
    

正则解析

- 将煎蛋网中的图片数据进行爬取且存储在本地 :

import re
import os

dirName = './imgLibs'
if not os.path.exists(dirName):
    os.mkdir(dirName)
    
url = 'http://jandan.net/pic/MjAxOTEwMDktNjY=#comments'
page_text = requests.get(url,headers=headers).text
#解析数据:img标签的src的属性值
ex = '<div class="text">.*?<img src="(.*?)" referrerPolicy.*?</div>'
img_src_list = re.findall(ex,page_text,re.S)
for src in img_src_list:
    if 'org_src' in src:
        src = re.findall('org_src="(.*?)" onload',src)[0]
    src = 'http:'+src
    imgName = src.split('/')[-1]
    imgPath = dirName+'/'+imgName
    request.urlretrieve(src,imgPath)
    print(imgName,'下载成功!!!')

bs4解析

  • - 环境的安装:
      - pip install bs4
      - pip install lxml
    - bs4的解析原理:
      - 实例化一个BeautifulSoup的一个对象,把即将被解析的页面源码数据加载到该对象中
      - 需要调用BeautifulSoup对象中的相关的方法和属性进行标签定位和数据的提取
    - BeautifulSoup的实例化
      - BeautifulSoup(fp,'lxml'):将本地存储的html文档中的页面源码数据加载到该对象中
      - BeautifulSoup(page_text,'lxml'):将从互联网中请求道的页面源码数据加载到改对象中
    - 标签的定位
      - soup.tagName:只可以定位到第一个tagName标签
      - 属性定位:soup.find('tagName',attrName='value'),只可以定位到符合要求的第一个标签
        - findAll:返回值是一个列表。可以定位到符合要求的所有标签
      - 选择器定位:soup.select('选择器')
        - 选择器:id,class,tag,层级选择器(大于号表示一个层级,空格表示多个层级)
    - 取文本
      - text:将标签中所有的文本取出
      - string:将标签中直系的文本取出
    - 取属性
      - tag['attrName']
    
    
from bs4 import BeautifulSoup
fp = open('./test.html',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
# soup.div
# soup.find('div',class_='song')
# soup.findAll('div',class_='song')
# soup.select('#feng')[0]
# soup.select('.tang > ul > li > a')
# soup.select('.tang a')
# tag = soup.b
# tag.string
# div_tag = soup.find('div',class_='tang')
# div_tag.text
a_tag = soup.select('#feng')[0]
a_tag

- 使用bs4解析三国演义小说的标题和内容,存储到本地 :

main_url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
page_text = requests.get(url=main_url,headers=headers).text
#数据解析:章节的标题和详情页的url
soup = BeautifulSoup(page_text,'lxml')
a_list = soup.select('.book-mulu > ul > li > a')
fp = open('./sanguo.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
    #数据解析:章节内容
    detail_soup = BeautifulSoup(detail_page_text,'lxml')
    div_tag = detail_soup.find('div',class_='chapter_content')
    content = div_tag.text
    
    fp.write(title+':'+content+'
')
    print(title,'写入成功!!!')
fp.close()

xpath解析

  • - 环境的安装
      - pip install lxml
    - 解析原理
      - 实例化一个etree的对象,且把即将被解析的页面源码数据加载到该对象中
      - 调用etree对象中的xpath方法结合这不同形式的xpath表达式进行标签定位和数据提取
    - etree对象的实例化
      - etree.parse('fileName')  - 本地文档
      - etree.HTML(page_text)	- 网络请求
    - 标签定位
      - 最左侧的/:一定要从根标签开始进行标签定位
      - 非最左侧的/:表示一个层级
      - 最左侧的//:可以从任意位置进行指定标签的定位
      - 非最左侧的//:表示多个层级
      - 属性定位://tagName[@attrName="value"]
      - 索引定位://tagName[@attrName="value"]/li[2],索引是从1开始
      - 逻辑运算:
        - 找到href属性值为空且class属性值为du的a标签
        - //a[@href="" and @class="du"]
      - 模糊匹配:
        - //div[contains(@class, "ng")]
        - //div[starts-with(@class, "ta")]
    - 取文本
      - /text():直系的文本内容
      - //text():所有的文本内容
    - 取属性
      - /@attrName
    
    
from lxml import etree
tree = etree.parse('./test.html')
# tree.xpath('/html//title')
# tree.xpath('//div')
# tree.xpath('//div[@class="tang"]')
# tree.xpath('//div[@class="tang"]/ul/li[2]')
# tree.xpath('//p[1]/text()')
# tree.xpath('//div[@class="song"]//text()')
tree.xpath('//img/@src')[0]

  • 需求:爬取虎牙主播名称,热度和标题
url = 'https://www.huya.com/g/xingxiu'
page_text = requests.get(url=url,headers=headers).text

#数据解析
tree = etree.HTML(page_text)
li_list = tree.xpath('//div[@class="box-bd"]/ul/li')
for li in li_list:
    #实现的是页面局部数据的指定数据的解析
    title = li.xpath('./a[2]/text()')[0]
    author = li.xpath('./span/span[1]/i/text()')[0]
    hot = li.xpath('./span/span[2]/i[2]/text()')[0]
    
    print(title,author,hot)

  • 爬取http://pic.netbian.com/4kmeinv/中前五页的图片数据
    • 中文乱码的处理
    • 多页码数据的爬取
# url = 'http://pic.netbian.com/4kmeinv/' #第一页
#指定一个通用的url模板:不可变的
url = 'http://pic.netbian.com/4kmeinv/index_%d.html'
dirName = './MZLib'
if not os.path.exists(dirName):
    os.mkdir(dirName)
    
for page in range(1,6):
    if page == 1:
        new_url = 'http://pic.netbian.com/4kmeinv/'
    else:
        new_url = format(url%page)
    page_text = requests.get(url=new_url,headers=headers).text
    #数据解析:图片地址&图片名称
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//div[@class="slist"]/ul/li')
    for li in li_list:
        img_name = li.xpath('./a/img/@alt')[0]
        img_name = img_name.encode('iso-8859-1').decode('gbk')+'.jpg'
        img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
        img_data = requests.get(img_src,headers=headers).content #图片的二进制类型数据
        img_path = dirName+'/'+img_name
        with open(img_path,'wb') as fp:
            fp.write(img_data)
    print('第{}页爬取完毕!!!'.format(page))          

url = 'https://www.aqistudy.cn/historydata/'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
# hot_cities = tree.xpath('//div[@class="bottom"]/ul/li/a/text()')
# all_cities = tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text()')
tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text() | //div[@class="bottom"]/ul/li/a/text()')

7. 代理

代理指的就是代理服务器
代理的作用 : 
	请求和响应数据的转发
代理和爬虫之间的关联 :
	可以基于代理实现更换爬虫程序请求的ip地址
代理网站 :
	1. 西祠 https://www.xicidaili.com/nn/
	2. 快代理
	3. www.goubanjia.comm
	4. 代理精灵 http://http.zhiliandaili.cn/
代理的匿名度 :
	高匿 : 所访问的服务器察觉不到是否是代理访问,也无法知晓真正访问的ip
	匿名 : 所访问的服务器知道是代理访问,但无法查到真正的ip
	透明 : 知道是代理,并且知道真实ip
类型 :
	http
	https

# 使用代理发请求 
import requests
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
    'Connection':'close'
}
url = 'https://www.baidu.com/s?ie=UTF-8&wd=ip'
page_text = requests.get(url,headers=headers,proxies={'https':'125.87.99.237:22007'}).text
with open('./ip.html','w',encoding='utf-8') as fp:
    fp.write(page_text)

  • 搭建一个免费的代理池 (利用付费代理ip爬取免费代理网站的ip)
#构建一个付费的代理池
import random
ips_pool = []
url = 'http://ip.11jsq.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=103&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=2'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
ip_list = tree.xpath('//body//text()')
for ip in ip_list:
    dic = {'https':ip}
    ips_pool.append(dic)

from lxml import etree
url = 'https://www.xicidaili.com/nn/%d' #通用的url模板(不可变)
all_ips = []
for page in range(1,5):
    new_url = format(url%page)
    page_text = requests.get(new_url,headers=headers,proxies=random.choice(ips_pool)).text
    tree = etree.HTML(page_text)
    #在xpath表达式中不可以出现tbody标签
    tr_list = tree.xpath('//*[@id="ip_list"]//tr')[1:]
    for tr in tr_list:
        ip = tr.xpath('./td[2]/text()')[0]
        port = tr.xpath('./td[3]/text()')[0]
        type_ip = tr.xpath('./td[6]/text()')[0]
        dic = {
            'ip':ip,
            'port':port,
            'type':type_ip
        }
        all_ips.append(dic)
                
print(len(all_ips))

8. cookie

需求:将https://xueqiu.com/中的新闻数据进行爬取
爬虫中处理cookie的操作
    手动处理:将cookie写在headers中
    自动处理:session对象。
获取session对象:requests.Session()
作用:
	session对象和requests对象都可以对指定的url进行请求发送。只不过使用session进行请求发送的过程中如果产生了cookie则cookie会被自动存储在session对象中

url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=20352188&count=15&category=-1'

news_json = requests.get(url,headers=headers).json()
news_json

#基于cookie操作的修正
session = requests.Session()
url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=20352188&count=15&category=-1'
#将cookie存储到session中,目的是将cookie获取存储到session中
session.get('https://xueqiu.com/',headers=headers) 

#保证该次请求时携带对应的cookie才可以请求成功
news_json = session.get(url,headers=headers).json()
news_json

9. 验证码的识别

使用线上的打码平台进行自动的识别:
    - 云打码
    - 超级鹰 :
        - 注册《用户中心》身份的账户
        - 登陆
            - 创建一个软件
            - 下载示例代码《开发文档》

import requests
from hashlib import md5

class Chaojiying_Client(object):

    def __init__(self, username, password, soft_id):
        self.username = username
        password =  password.encode('utf8')
        self.password = md5(password).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }

    def PostPic(self, im, codetype):
        """
        im: 图片字节
        codetype: 题目类型 参考 http://www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
        return r.json()

    def ReportError(self, im_id):
        """
        im_id:报错题目的图片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()
    
chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')	#用户中心>>软件ID 生成一个替换 96001
im = open('a.jpg', 'rb').read()													#本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
print(chaojiying.PostPic(im,1004)['pic_str'])	




#验证码识别函数的封装
def transformCode(imgPath,imgType):
    chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')
    im = open(imgPath, 'rb').read()
    return chaojiying.PostPic(im,imgType)['pic_str']

模拟登陆

版本一 :

版本一的问题 :

请求需要有动态的参数

通常请情况下动态变化的请求参数都会被隐藏在前台页面源码中

from urllib import request

#验证码的识别:将验证码下载到本地然后提交给打吗平台进行识别
main_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = requests.get(main_url,headers=headers).text
tree = etree.HTML(page_text)
code_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
request.urlretrieve(code_src,'./code.jpg')

#识别验证码
code_text = transformCode('./code.jpg',1004)


login_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
data = {
    '__VIEWSTATE': '8/BKAQBaZHn7+GP+Kl2Gx43fFO1NI32RMyVae0RyrtFQue3IAhzQKvkml41cIT42Y//OcQccA8AqGYkvB+NFkU43uaHqU69Y0Z1WT3ZRrr4vR+CF7JlBG29POXM=',
    '__VIEWSTATEGENERATOR': 'C93BE1AE',
    'from': 'http://so.gushiwen.org/user/collect.aspx',
    'email': 'www.zhangbowudi@qq.com',
    'pwd': 'bobo328410948',
    'code': code_text,
    'denglu': '登录',
}
print(code_text)
page_text = requests.post(login_url,headers=headers,data=data).text

with open('./login.html','w',encoding='utf-8') as fp:
    fp.write(page_text)

版本二 :

版本二遇到的问题 :

​ 没有携带cookie ,且这个网站的cookie在验证码的请求里

#验证码的识别:将验证码下载到本地然后提交给打吗平台进行识别
main_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = requests.get(main_url,headers=headers).text
tree = etree.HTML(page_text)
code_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
request.urlretrieve(code_src,'./code.jpg')

#解析出动态变化的请求参数
__VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR = tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]/@value')[0]

#识别验证码
code_text = transformCode('./code.jpg',1004)


login_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
data = {
    '__VIEWSTATE': __VIEWSTATE,
    '__VIEWSTATEGENERATOR': __VIEWSTATEGENERATOR,
    'from': 'http://so.gushiwen.org/user/collect.aspx',
    'email': 'www.zhangbowudi@qq.com',
    'pwd': 'bobo328410948',
    'code': code_text,
    'denglu': '登录',
}
print(code_text)
page_text = requests.post(login_url,headers=headers,data=data).text

with open('./login.html','w',encoding='utf-8') as fp:
    fp.write(page_text)

版本三 (完美版):

s = requests.Session()
#验证码的识别:将验证码下载到本地然后提交给打吗平台进行识别
main_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = s.get(main_url,headers=headers).text
tree = etree.HTML(page_text)
code_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]

# request.urlretrieve(code_src,'./code.jpg')
code_data = s.get(code_src,headers=headers).content
with open('./code.jpg','wb') as fp:
    fp.write(code_data)

#解析出动态变化的请求参数
__VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR = tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]/@value')[0]

#识别验证码
code_text = transformCode('./code.jpg',1004)


login_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
data = {
    '__VIEWSTATE': __VIEWSTATE,
    '__VIEWSTATEGENERATOR': __VIEWSTATEGENERATOR,
    'from': 'http://so.gushiwen.org/user/collect.aspx',
    'email': 'www.zhangbowudi@qq.com',
    'pwd': 'bobo328410948',
    'code': code_text,
    'denglu': '登录',
}
print(code_text)
page_text = s.post(login_url,headers=headers,data=data).text

with open('./login.html','w',encoding='utf-8') as fp:
    fp.write(page_text)
- 反爬机制
    - robots
    - UA检测
    - 图片懒加载 
    - 代理
    - cookie
    - 验证码
    - 动态变化的请求参数
    - 动态加载的数据

10. 使用线程池提升爬取数据的效率

# 同步操作

import time

start = time.time()
def request(url):
    print('正在请求',url)
    time.sleep(2)
    print('请求完毕:',url)
urls = [
    'www.1.com',
    'www.b.com',
    'www.3.com'
]

for url in urls:
    request(url)
print('总耗时:',time.time()-start)
# 异步操作

import time
from multiprocessing.dummy import Pool

start = time.time()
pool = Pool(3)
def request(url):
    print('正在请求',url)
    time.sleep(2)
    print('请求完毕:',url)

urls = [
    'www.1.com',
    'www.b.com',
    'www.3.com'
]

pool.map(request,urls)

print('总耗时:',time.time()-start)
# 爬虫+ 线程池
# server端
from flask import Flask,render_template
from  time import sleep
app = Flask(__name__)

@app.route('/bobo')
def index_bobo():
    sleep(2)
    return render_template('ip.html')

@app.route('/jay')
def index_jay():
    sleep(2)
    return render_template('login.html')
app.run()

# 爬虫 + 线程池
import time
from multiprocessing.dummy import Pool
import requests
from lxml import etree
start = time.time()
urls = [
    'http://localhost:5000/jay',
    'http://localhost:5000/bobo'
]

def get_request(url):
    page_text = requests.get(url).text
    return page_text


def parse(page_text):
    tree = etree.HTML(page_text)
    print(tree.xpath('//div[1]//text()'))


pool = Pool(2)
page_text_list = pool.map(get_request,urls)


pool.map(parse,page_text_list)


print(len(page_text_list))


print('总耗时:',time.time()-start)
原文地址:https://www.cnblogs.com/qmuy/p/11655044.html