Loading

爬虫之requests

爬虫概述

  • 什么是爬虫?

    • 通过编写程序让其模拟浏览器上网,然后去互联网中抓取数据的过程

  • 爬虫的分类

    • 通用爬虫:就是抓取一整张页面源码内容。

    • 聚焦爬虫:抓取的是页面中局部的内容

    • 增量式爬虫:可以监测网站数据更新的情况。抓取网站中最新更新出来的数据。

  • 反爬机制:对应的载体数网站。

  • 反反爬策略:对应的载体爬虫程序。

  • 探究一下爬虫的合法性:

    • 爬虫本身是不被法律禁止(中立性)

    • 爬取数据的违法风险的体现:

      • 爬虫干扰了被访问网站的正常运营

      • 爬虫抓取了受到法律保护的特定类型的数据或信息。

    • 如何规避违法的风险?

      • 严格遵守网站设置的robots协议;

      • 在规避反爬虫措施的同时,需要优化自己的代码,避免干扰被访问网站的正常运行;

      • 在使用、传播抓取到的信息时,应审查所抓取的内容,如发现属于用户的个人信息、隐私或者他人的商业秘密的,应及时停止并删除。

  • 第一个反爬机制:robots协议

    • 特性:防君子不防小人

https和http相关

  • http协议:客户端和服务器端进行数据交互的形式。

  • 常用的请求头信息

    • User-Agent:请求载体的身份标识

    • Connection:close

  • 响应头信息

    • content-type

  • https:安全的http(加密)

    • 对称秘钥加密

    • 非对称秘钥加密

    • 证书秘钥加密(***):证书

requests模块

  • 作用:模拟浏览器发请求。

  • 编码流程:

    • 指定url

    • 发起请求,获取响应对象

    • 获取响应数据

    • 持久化存储

通用爬虫小练习

1.简易的网页采集器

import requests
url = 'https://www.sogou.com/web'
# 动态的参数
wd = input('enter a key word:')
param = { 'query':wd}
# 携带了动态参数进行的请求发送
response = requests.get(url=url,params=param)
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
   fp.write(page_text)
print(fileName,'下载成功!')

上述程序执行后:

  • 乱码:修改响应数据的编码

  • 数据丢失:UA检测反爬机制

  • UA检测:网站会检测当前请求的请求载体的身份标识

  • UA伪装:

    • 需要将User-Agent对应的数据封装到字典种,将字典作用的请求方法的headers参数中

# 简易的网页采集器
url = 'https://www.sogou.com/web'
# 动态参数
msg = input('enter a key word:')
params = { 'query':msg}
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36",}
# 携带了动态参数进行的请求发送
reponse = requests.get(url=url, params=params,headers=headers)
#可以手动修改响应数据的编码
response.encoding = 'utf-8'
page_text = reponse.text
filename = msg+'.html'
with open(filename,'w',encoding='utf8') as f:
    f.write(page_text)

 

2. 爬取豆瓣电影中电影详情数据

import requests
url = 'https://movie.douban.com/j/chart/top_list'
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36",
}
start = input('起始爱情电影:')
limit = input('TOP爱情电影:')
params = {
    "type":"13",
    "interval_id": "100:90",
    "action": "" ,
    "start": start,
    "limit":limit,
}
page_text = requests.get(url=url,params=params,headers=headers).json()
for dic in page_text:
    title,score = dic['title'],dic['score']
    print('电影名:{},评分:{}'.format(title,score))

 

动态加载的数据

  • 需要借助于抓包工具进行分析和动态加载数据对应url的提取

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

url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
city = input('请输入要查询城市的名称:')
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36",
}
for i in range(1,8):
    data = {
        "cname":"", 
        "pid": "",
        "keyword": city,
        "pageIndex": i,
        "pageSize": "10",
    }
    page_json = requests.post(url=url,headers=headers,data=data).json()
#     print(page_json)
    for dic in page_json['Table1']:
        rownum = dic['rownum']
        storeName = dic['storeName']
        addressDetail = dic['addressDetail']
        print('{}-{}-{}'.format(rownum,storeName,addressDetail))

4. 化妆品公司详情爬取

http://125.35.6.84:81/xk/

# 1.检测页面中的数据是否为动态加载
# 2.通过抓包工具找到动态加载数据对应的数据包,数据包中提取该请求的url
# 3.发现首页中动态加载的数据包中并没有详情页的url,但是有每一家企业的id
# 4.通过观察发现,每一家企业详情页的域名是一致的只有携带的id参数不同
# 5.可以通过相同的域名结合着不同企业的id组成一个企业详情页的url
# 6.通过抓包工具发现详情页中的企业详情数据是动态加载出来的
# 7.在抓包工具中通过全局搜索找到了动态加载数据对应的数据包,并且可以提取url
# 8.多家企业对应的详情数据对应的数据包的url都是一样的,只有携带的参数id的值不同
url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList'
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'
}
for page in range(1,11):
    data = {
        "on": "true",
        "page": str(page),
        "pageSize": "15",
        "productName": "",
        "conditionType": "1",
        "applyname": "",
        "applysn": "",
    }
    json_data = requests.post(url=url,data=data,headers=headers).json()
    print('第{}页爬取结束!'.format(page))
    for dic in json_data['list']:
        _id = dic.get('ID')
        #对企业详情数据对应的url发起一个post请求
        post_url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById'
        data = {'id':_id}
        detail_data = requests.post(url=post_url,headers=headers,data=data).json()
        company_name = detail_data['epsName']
        print(company_name)

5.爬取喜马拉雅的免费音频

import requests
url = 'https://www.ximalaya.com/revision/play/album?'
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'}
for num in range(1,10):
    params = {
        "albumId": "9742789",
        "pageNum": num,
        "sort": "0",
        "pageSize": "30",
    }
    json_data = requests.get(url=url,headers=headers,params=params).json()
    for dic in json_data.get('data').get('tracksAudioPlay'):
        trackName = dic['trackName']
        trackUrl = dic['trackUrl']
        src = dic['src']
        url = 'https://www.ximalaya.com{}'.format(trackUrl)
        print(url)
        xs_data = requests.get(url=src,headers=headers).content
    #     print(xs_data)
        with open('./{}.mp3'.format(trackName),'wb') as fp:
            fp.write(xs_data)

聚焦爬虫之数据解析

  • 实现数据解析的方式:

    • 正则

    • bs4

    • xpath

    • pyquery

  • 为什么要使用数据解析?

    • 数据解析是实现聚焦爬虫的核心技术,就是在一张页面源码中提取出指定的文本内容。

  • 数据解析的通用解析原理?

    • 我们要解析提取的数据都是存储在标签中间或者是标签的属性中

    • 1.标签定位

    • 2.取文本或者取

一、正则解析

1. 案例 爬糗事百科糗图

// 通过检查源码发现目标div标签
<div class="thumb"><a href="/article/121960143" target="_blank">
<img src="//pic.qiushibaike.com/system/pictures/12196/121960143/medium/5E2GWJ5SP18QQ051.jpg" alt="重要通知">
</a></div>
import re
import requests
import os
from urllib import request
# 通用的url模板不可变的
url = 'https://www.qiushibaike.com/pic/page/%d/?s=5205111'
if not os.path.exists('./qiutuPic'):
    os.mkdir('./qiutuPic')
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"}
for page in range(1,36):
    # 新的url地址
    new_url = format(url%page)
    # 重新发送请求
    page_text = requests.get(url=new_url,headers=headers).text
    # 数据解析:img的src的属性值(图片的连接)
    ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
    img_src_list = re.findall(ex,page_text,re.S) # 在这里使用正则的时候,必须有re.S参数
    for src in img_src_list:
        src = 'https:'+src
        img_name = src.split('/')[-1]
        img_path = './qiutuPic/'+img_name
        request.urlretrieve(src,img_path)
        print(img_name+'下载成功!')

 

二、bs4 解析

  • 解析原理:

    • 1.实例化一个BeautifulSoup的一个对象,且将即将被解析的页面源码加载到该对象中

    • 2.需要调用bs对象中相关属性和方法进行标签定位和数据的获取

  • 环境安装

    • pip install lxml(解析器)

    • pip install bs4

  • BeautifulSoup对象的实例化

    • BeautifulSoup('fp','lxml'):将本地存储的一张HTML页面中的页面源码加载到bs4对象中 fp文件句柄

    • BeautifulSoup(page_text,'lxml'):将互联网请求到的页面源码数据加载到bs4对象

bs相关属性和方法

  • soup.tagName:可以定位到第一次出现的tagName标签,返回值是一个单数

  • find('tagName') == soup.taagName

  • 属性定位:find('tagName',attrName='value'),返回的也是单数

  • find_all():和find的用法一样,只是返回值是一个列表

  • select('选择器'):id,class,标签,层级选择器,返回值为列表.>表示一个层级 空格表示多个层级

  • 取文本:string定位的是直系的文本内容,text,get_text()定位的是所有文本内容 两个获得东西是一样的

  • 取属性:tag['attrName']

二、bs4案例

1. 基础

from bs4 import BeautifulSoup
#使用bs解析本地存储的一张页面中相关的局部数据
fp = open('./test.html','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
#标签的定位
soup.a
soup.find('div',class_='tang')
soup.find_all('div')
soup.select('.song')
soup.select('.tang > ul > li > a')
soup.select('.tang > ul > li > a')[1].string
soup.find('div',class_='song').text
soup.find('div',class_='song').get_text()
soup.select('.song > img')[0]['src']

2. 爬取小说案例

  • 爬取诗词名句网中的西游记小说

    from bs4 import BeautifulSoup
    import requests
    url = 'http://www.shicimingju.com/book/xiyouji.html'
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"}
    page_text = requests.get(url,headers=headers).text
    # 数据解析
    soup = BeautifulSoup(page_text,'lxml')
    a_list = soup.select('.book-mulu > ul > li > a') # 或者可以用 ./book-mulu li > a
    fp = open('./西游记.edub','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+':'+content+'\n')
        print(title,'下载成功!!')
    fp.close()

三、xpath解析

1.介绍

  • 优点:通用性强

  • 解析原理:

    • 1.实例化一个etree的对象,将即将被解析的页面加载到该对象中

    • 2.调用etree对象中的xpath方法结合着不同的xpath表达式实现标签定位和数据提取

  • 环境安装:

    • pip install lxml

  • etree对象实例化:

    • etree.parse('filePath')

    • etree.HTML(page_text)

2. xpath表达式

  • xpath方法返回值是列表.

  • 最左侧如果为一个斜杠,则表示必须从跟节点开始进行标签定位

  • 最左侧为两个斜杠,表示可以从任意位置标签定位

  • 非最左侧的一个斜杠表示一个层级,两个斜杠表示多个层级

  • 属性定位://tagName[@attrName='value']

  • 索引定位://div[@class="tang"]/ul/li[2] 索引是从1开始

  • 取文本: /text() //text()

  • 取属性:/@attrName

3. xpath案例

1 .boss爬虫岗位爬取

from lxml import etree
import requests
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"}
url = 'https://www.zhipin.com/c101010100/?query=python爬虫&page=%d'
for page in range(1,5):
    new_url = format(url%page)
    page_text = requests.get(url=new_url,headers=headers).text
    # 解析
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//*[@id="main"]/div/div[3]/ul/li')
    for li in li_list:
        job_name = li.xpath('./div/div[@class="info-primary"]/h3/a/div[1]/text()')[0]
        salary = li.xpath('./div/div[@class="info-primary"]/h3/a/span/text()')[0]
        company = li.xpath('./div/div[2]/div/h3/a/text()')[0]
        detail_url = 'https://www.zhipin.com'+li.xpath('./div/div[1]/h3/a/@href')[0]
        detail_page_text = requests.get(url=detail_url,headers=headers).text
        # 详情页工作描述
        tree = etree.HTML(detail_page_text)
        job_desc = tree.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()')
        job_desc = ''.join(job_desc)
        print(job_name,salary,company,job_desc)
  1. 中文乱码问题

#通用的url模板(不可变)
url = 'http://pic.netbian.com/4kdongwu/index_%d.html'
for page in range(1,11):
    if page == 1:
        new_url = 'http://pic.netbian.com/4kdongwu/'
    else:
        new_url = format(url%page)
    response = requests.get(new_url,headers=headers)
#     response.encoding = 'utf-8'
    page_text = response.text
    
    #解析:图片名称和图片的地址
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//div[@class="slist"]/ul/li')
    for li in li_list:
        img_title = li.xpath('./a/img/@alt')[0]
        #通用解决中文乱码的处理方式
        img_title = img_title.encode('iso-8859-1').decode('gbk')
        img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0] 

3.xpath之管道符|

#爬取https://www.aqistudy.cn/historydata/热门城市和全部城市的城市名称
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()')
# print(all_cities)
​
#好处:使得xpath表达式更具有通用性
tree.xpath('//div[@class="bottom"]/ul/li/a/text() | //div[@class="bottom"]/ul/div[2]/li/a/text()')
# 糗事百科中爬取作者名 匿名的处理
url = 'https://www.qiushibaike.com/text/page/%d/'
for page in range(1,10):
    print(str(page)+'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
    new_url = format(url%page)
    page_text = requests.get(url=new_url,headers=headers).text
    
    tree = etree.HTML(page_text)
    div_list = tree.xpath('//div[@id="content-left"]/div')
    for div in div_list:
        author = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()')[0]
        print(author)

4. 图片懒加载

  • 图片懒加载是一种网页优化技术。图片作为一种网络资源,在被请求时也与普通静态资源一样,将占用网络资源,而一次性将整个页面的所有图片加载完,将大大增加页面的首屏加载时间。为了解决这种问题,通过前后端配合,使图片仅在浏览器当前视窗内出现时才加载该图片,达到减少首屏图片请求数的技术就被称为“图片懒加载”。

  • 网站一般如何实现图片懒加载技术呢?

  • 在网页源码中,在img标签中首先会使用一个“伪属性”(通常使用src2,original......)去存放真正的图片链接而并非是直接存放在src属性中。当图片出现到页面的可视化区域中,会动态将伪属性替换成src属性,完成图片的加载。

    import requests
    from lxml import etree
    url = 'http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html'
    page_text = requests.get(url,headers=headers).text
    for page in range(1,10):
        if page == 1:
            new_url = 'http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html'
    tree = etree.HTML(page_text)
    div_list = tree.xpath('//div[@id="container"]/div')
    for div in div_list:
        img_src = div.xpath('./div/a/img/@src2')[0]
        print(img_src)

5. 爬取简历并持久化存储

import os
import requests
from lxml import etree
from urllib import request
if not os.path.exists('./简历'):
    os.mkdir('./简历')
url = 'http://sc.chinaz.com/jianli/free_%d.html'
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"}
for page in range(1,20):
    if page == 1:
        new_url = 'http://sc.chinaz.com/jianli/free.html'
    else:
        new_url = format(url%page)
    page_text = requests.get(url=new_url,headers=headers).text
    tree = etree.HTML(page_text)
    div_list = tree.xpath('//*[@id="container"]')
    for div in div_list:
        a_href = div.xpath('./div/a/@href')[0]
        a_name = div.xpath('./div/p/a/text()')[0]
        a_name = a_name.encode('iso-8859-1').decode('utf8')
        detail_text = requests.get(url=a_href,headers=headers).text
        tree = etree.HTML(detail_text)
        detail_url = tree.xpath('//*[@id="down"]/div[2]/ul/li[1]/a/@href')[0]
        jianli_name = a_name
        jianli_path = './简历/'+jianli_name+'.rar'
        request.urlretrieve(detail_url,jianli_path)
        print(jianli_name+'下载成功!')
 

代理ip

  • 代理

  • cookie

  • 模拟登陆

    • 验证码的识别

  • 线程池在requests的应用

  • 单线程+多任务异步协程

  • 代理: 代理服务器

  • 基于代理的网站:

    • 站大爷

    • goubanjia

    • 快代理

    • 西祠代理

  • 代理的匿名度

    • 透明:使用透明代理,对方服务器可以知道你使用了代理,并且也知道你的真实IP

    • 匿名:对方服务器可以知道你使用了代理,但不知道你的真实IP。

    • 高匿:对方服务器不知道你使用了代理,更不知道你的真实IP

  • 类型:

    • http:代理服务器只可以转发http协议的请求

    • https:代理服务器只可以转发https协议的请求

  • 编码:

    • 在get或者post方法中应用一个proxies的参数,给其参数赋值为{'http':'ip:port'}

cookie的处理方式

  • 处理方式:

    • 手动处理:将cookie的键值对手动添加到headers字典中,然后将headers作用到get或者post方法的headers参数中

    • 自动处理:使用Session。

      • session作用:session可以和requests一样进行请求的发送

      • 特性:使用session发起请求,如果请求过程中产生cookie,则cookie会被自动存储到session中

url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=-1&count=10&category=-1'
#创建一个session对象
session = requests.Session()
#获取cookie
session.get('https://xueqiu.com/',headers=headers)
#携带cookie进行的请求发送
session.get(url,headers=headers).json()

模拟登陆

  • 什么是模拟登陆

    • 使用requests对登陆按钮的请求进行发送

  • 为什么要模拟登陆

    • 有的页面必须登陆之后才显示

  • 超级鹰的使用流程

    • 注册:用户中心身份的账户

    • 登陆:

      • 查看提分的剩余

      • 创建一个软件ID:软件ID-》生成一个软件ID(ID的名称和说明)

      • 下载示例代码:开发文档-》选择语言-》点击下载

 

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() #识别古诗文网中的验证码图片 url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx' headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36' } page_text = requests.get(url,headers=headers).text tree = etree.HTML(page_text) code_img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0] img_data = requests.get(code_img_src,headers=headers).content with open('./code.jpg','wb') as fp: fp.write(img_data) #使用线上平台识别验证码 chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用户中心>>软件ID 生成一个替换 96001 im = open('code.jpg', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要// print(chaojiying.PostPic(im, 1902)['pic_str'])

1.案例-基于古诗文网的模拟登陆

#识别古诗文网中的验证码图片
s = requests.Session()
​
url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = s.get(url,headers=headers).text
tree = etree.HTML(page_text)
code_img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
#使用session对验证码图片发请求(会产生cookie)
img_data = s.get(code_img_src,headers=headers).content
with open('./code.jpg','wb') as fp:
    fp.write(img_data)
   
# 解析出动态参数的数据值
__VIEWSTATE = tree.xpath('//input[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR = tree.xpath('//input[@id="__VIEWSTATEGENERATOR"]/@value')[0]
    
#使用线上平台识别验证码
chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')             # 用户中心>>软件ID 生成一个替换 96001
im = open('code.jpg', 'rb').read()                              #  本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
#验证码图片的文本数据
code_img_text = chaojiying.PostPic(im, 1902)['pic_str']
print(code_img_text)
#模拟登陆
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_img_text,
    "denglu": "登录",
}
login_page_text = s.post(login_url,headers=headers,data=data).text
with open('gushiwen.html','w',encoding='utf-8') as fp:
    fp.write(login_page_text)

线程池的应用

- 异步操作可以和非异步操作结合使用
- 线程池最好只被应用在较为耗时的操作中

同步:

def request(url):
    print('正在请求:',url)
    time.sleep(2)
    print('请求成功:',url)
start = time.time()
urls = [
    'www.1.com',
    'www.2.com',
    'www.3.com',
    'www.4.com',
]
for url in urls:
    request(url)    
print(time.time()-start)

基于异步的操作:

from multiprocessing.dummy import Pool
start = time.time()
pool = Pool(4)
def request(url):
    print('正在请求:',url)
    time.sleep(2)
    print('请求成功:',url)
urls = [
    'www.1.com',
    'www.2.com',
    'www.3.com',
    'www.4.com',
]
pool.map(request,urls)
pool.close()
pool.join()
print(time.time()-start)

2.案例-爬取梨视频的短视频数据

import requests
from lxml import etree
import re
#爬取梨视频的短视频数据
#var contId="1570697",liveStatusUrl="liveStatus.jsp",liveSta="",
# playSta="1",autoPlay=!1,isLiving=!1,isVrVideo=!1,hdflvUrl="",sdflvUrl="",hdUrl="",sdUrl="",ldUrl="",
# srcUrl="https://video.pearvideo.com/mp4/adshort/20190626/cont-1570697-14061078_adpkg-ad_hd.mp4",vdoUrl=srcUrl,skinRes="//www.pearvideo.com/domain/skin",videoCDN="//video.pearvideo.com";
pool = Pool(4)
​
url = 'https://www.pearvideo.com/category_1'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
li_list = tree.xpath('//*[@id="listvideoListUl"]/li')
all_video_urls = []
for li in li_list:
    detail_url = 'https://www.pearvideo.com/'+li.xpath('./div/a/@href')[0]
    detail_page_text = requests.get(detail_url,headers=headers).text
    #解析出视频的url
    ex = 'srcUrl="(.*?)",vdoUrl'
    video_url = re.findall(ex,detail_page_text,re.S)[0]
    all_video_urls.append(video_url)
    
def dowmload_save(url):
    video_data = requests.get(url,headers=headers).content
    fileName = url.split('/')[-1]
    with open(fileName,'wb') as fp:
        fp.write(video_data)
    print(fileName,'下载成功')
    
#视频数据的请求和持久化存储是比较耗时的,可以基于线程池来实现
pool.map(dowmload_save,all_video_urls)#参数1对应的函数必须只可以有一个参数
 

 

多任务异步协程(并发)

- 协程:对象。如果一个函数在定义的时候被async修饰了,则该函数被调用的时候会返回一个协程对象
,函数内部的程序语句不会其调用的时候被执行(特殊的函数)

- 任务对象:对象,就是对协程的进一步封装。任务对象==协程==特殊的函数.任务对象中可以显示
协程的相关状态。任务对象可以被绑定一个回调。
- 绑定回调:

- 事件循环:无限(不确定循环次数)的循环。需要向其内部注册多个任务对象(特殊的函数)。
- async:专门用来修饰函数
- await:挂起

- requests和aiohttp 的区别
- session.get、post(url,headers,params/data,proxy="http://ip:port")    ###  aiohttp
- response.text()、json().read()

案例1 多任务异步协程爬取音频

import aiohttp
import requests
import asyncio

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
}
url = 'https://www.ximalaya.com/revision/play/album?albumId=11219576&pageNum=1&sort=1&pageSize=30'
json_data = requests.get(url, headers=headers).json()

urls = []  # 30个音频的url和name
for dic in json_data['data']['tracksAudioPlay']:
    audio_url = dic['src']
    audio_name = dic['trackName']
    urls.append({'name': audio_name, 'url': audio_url})


# 定义特殊的修饰函数用来发送请求
async def request(dic):
    async with aiohttp.ClientSession() as s:
        # s是aiohttp中的一个请求对象
        # await:阻塞操作对应的代码中(请求,获取响应数据)
        # async:只要是跟aiohttp相关联的代码前
        # proxy='http://ip:port' 代理操作
        async with await s.get(dic['url'], headers=headers) as response:
            # text()字符串形式的响应数据
            # json(),read()二进制类型的数据
            audio_data = await response.read()
            name = dic['name']
            return {'data': audio_data, 'name': name}


# 回调函数的封装:必须有一个task的参数
def saveData(task):
    print('开始保存')
    dic = task.result()  # 音频的数据和名字  async修饰函数的返回值
    filename = dic['name'] + '.m4a'  #
    data = dic['data']
    with open(f'./相声/{filename}', 'wb') as f:
        f.write(data)
    print(filename, '下载完成!!')


tasks = []  # 任务列表
for dic in urls:
    # 协程
    c = request(dic)
    # 任务对象
    task = asyncio.ensure_future(c)
    task.add_done_callback(saveData)
    tasks.append(task)
# 创建事件循环对象,然后进行任务对象的注册,且启动事件循环
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

selenium

使用实例 京东joy

from selenium import webdriver
from time import sleep
# 实例化了一个谷歌浏览器对象且将驱动程序进行了加载
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
# 发起请求
bro.get('https://hellojoy.jd.com/')
# 使用find系列的函数进行标签定位
input_tag = bro.find_element_by_id('key')
# 进行节点交互
input_tag.send_keys('macbook')
sleep(2)
# 搜索按钮的定位
btn = bro.find_element_by_xpath('//*[@id="search-2014"]/div/button')
btn.click()
sleep(10)
# 执行js,实现滑动窗口
bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
sleep(2)
bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
sleep(2)

bro.close()

案例1 使用selenuim对动态数据进行爬取数据

# 动态请求数据
from bs4 import BeautifulSoup
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
bro.get('http://125.35.6.84:81/xk/')
# 获取当前页面中展示的企业名称
# 获取浏览器打开页面的页面源码数据(可见即可爬)
page_text = bro.page_source
# 使用bs4 解析企业名称
soup = BeautifulSoup(page_text,'lxml')
dl_list = soup.select('#gzlist > li > dl')
for dl in dl_list:
    name = dl.string
    print(name)
sleep(2)
bro.close()

案例2 使用selenuim多页处理

#处理多页
from selenium import webdriver
from time import sleep
#实例化了一个谷歌浏览器对象且将驱动程序进行了加载
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
bro.get('http://125.35.6.84:81/xk/')
alist = [] #存放不同页码对应的页面源码数据(page_source)
#获取当前页面中展示的企业名称
sleep(2)
#获取浏览器打开页面的页面源码数据(可见即可爬)
page_text = bro.page_source
alist.append(page_text)
id_value_model = 'pageIto_first%d'
for page in range(2,8):
    id_value = format(id_value_model%page)
    btn = bro.find_element_by_id(id_value)
    btn.click()
    sleep(3)
    page_text = bro.page_source
    alist.append(page_text)
    sleep(2)    
for page_text in alist:
    sleep(1)
    soup = BeautifulSoup(page_text,'lxml')
    dl_list = soup.select('#gzlist > li > dl')
    for dl in dl_list:
        name = dl.string
        print(name)        
bro.quit()

案例3 模拟登陆

#模拟登陆
from selenium import webdriver
from time import sleep
#实例化了一个谷歌浏览器对象且将驱动程序进行了加载
bro = webdriver.Chrome(executable_path='./chromedriver.exe')

bro.get('https://qzone.qq.com/')
#分析发现定位的a标签是出现在iframe标签之下,则必须通过switch_to.frame操作后,才可以进行标签定位
bro.switch_to.frame('login_frame')

a_tag = bro.find_element_by_id('switcher_plogin')
a_tag.click()
sleep(2)
bro.find_element_by_id('u').send_keys('123456')
sleep(2)
bro.find_element_by_id('p').send_keys('XXXXXXXXXXXXX')
sleep(2)
bro.find_element_by_id('login_button').click()

#登陆成功后的页面源码数据
page_text = bro.page_source
sleep(2)
bro.quit()

不太常用的操作

  • 动作链

from selenium import webdriver
from selenium.webdriver import ActionChains
from time import sleep
#实例化了一个谷歌浏览器对象且将驱动程序进行了加载
bro = webdriver.Chrome(executable_path='./chromedriver.exe')

bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')

#基于动作链实现拖动操作
bro.switch_to.frame('iframeResult')
#定位即将被拖动的标签
div_tag = bro.find_element_by_id('draggable')

#实例化一个动作链对象,将当前浏览器对象作为参数进行传入
action = ActionChains(bro)
#点击且长按的操作
action.click_and_hold(div_tag)

for i in range(5):
    #perform()表示立即执行动作链
    action.move_by_offset(15,0).perform()
    sleep(0.5)    
action.release()
sleep(2)
bro.quit()
  • phantomJs:是一个无可视化界面的浏览器

  • 谷歌无头浏览

from selenium import webdriver
from time import sleep

from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')


#实例化了一个谷歌浏览器对象且将驱动程序进行了加载
bro = webdriver.Chrome(executable_path='./chromedriver.exe',chrome_options=chrome_options)
bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')

bro.save_screenshot('./1.png')

print(bro.page_source)

如何规避selenium被检测的风险

from selenium.webdriver import Chrome
from selenium.webdriver import ChromeOptions


option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])

driver = Chrome(executable_path='./chromedriver.exe',options=option)

案例4 12306模拟登陆

超级鹰接口
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()
from selenium import webdriver
import time
import requests
from selenium.webdriver import ActionChains
from PIL import Image

# Pillow  == PIL

bro = webdriver.Chrome(executable_path='./chromedriver.exe')
bro.get('https://kyfw.12306.cn/otn/login/init')

time.sleep(3)

# 定位到了img标签(验证码),想要通过img标签获取验证码图片的左上角和右下角两点坐标
code_img_ele = bro.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
time.sleep(3)
# 获取验证码图片的左上角和右下角两点坐标
location = code_img_ele.location  # 验证码图片左上角坐标
print(location, ':左上角坐标!')

size = code_img_ele.size  # 返回的是验证码的尺寸(长和宽)
print(size, ':size的值')
# 矩形区域:表示的就是验证码图片的区域(裁剪的区域)
rangle = (
    int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))

# 将浏览器打开的登录页面进行整体截图
bro.save_screenshot('aa.png')

i = Image.open('./aa.png')
# 验证码图片的名称
code_img_name = 'code.png'
# 根据制定好的矩形区域(左上和右下两点坐标)进行验证码图片的裁剪
frame = i.crop(rangle)

frame.save(code_img_name)

chaojiying = Chaojiying_Client('martin144', 'martin144', '900215')

# 用户中心>>软件ID 生成一个替换 96001
im = open('./code.png', 'rb').read()
result = chaojiying.PostPic(im, 9004)['pic_str']
# x1,y1      x1,y1|x2,y2   55,99
#  x1,y1|x2,y2  ==》 [[x1,y1],[x2,y2]]
all_list = []  # 存储的是超级鹰返回的坐标数据
if '|' in result:
    list_1 = result.split('|')
    count_1 = len(list_1)
    for i in range(count_1):
        xy_list = []
        x = int(list_1[i].split(',')[0])
        y = int(list_1[i].split(',')[1])
        xy_list.append(x)
        xy_list.append(y)
        all_list.append(xy_list)
else:
    x = int(result.split(',')[0])
    y = int(result.split(',')[1])
    xy_list = []
    xy_list.append(x)
    xy_list.append(y)
    all_list.append(xy_list)
print(all_list)

# 根据all_list中的数据进行点击操作
action = ActionChains(bro)

for l in all_list:
    x = l[0]
    y = l[1]
    action.move_to_element_with_offset(code_img_ele, x, y).click().perform()
    time.sleep(1)

bro.find_element_by_id('username').send_keys('123ertghjk') # 12306账号
time.sleep(2)
bro.find_element_by_id('password').send_keys('asdfghjka') # 12306密码
time.sleep(2)
bro.find_element_by_id('loginSub').click()
time.sleep(10)
bro.quit()

 

 
posted @ 2019-06-25 20:14  martin_z  阅读(403)  评论(0编辑  收藏  举报