爬虫进阶模块篇

13.selenium

1.简介

#selenium最初是一个自动化测试工具,
而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题 
selenium本质是通过驱动浏览器, 
完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果,可支持多种浏览器
#selenium和爬虫之间的关联:
    - 模拟登录
    - 便捷的捕获到动态加载的数据(重点)
        - 特点:可见及可得
        - 缺点:效率低
#查看ajax和js里面存在动态加载数据
requests模块动态捕获加载数据 需要使用 使用请求头发送数据
#便捷的捕获到动态加载的数据(重点)
        - 特点:可见及可得
        - 缺点:效率低

注意点

#NoSuchElementException:定位的标签是存在与iframe之中,则就会抛出这个错误
#解决方法:switch_to.frame进行指定子页面的切换
bro.switch_to.frame('iframeResult')

2.selenium的具体使用

1.selenium
	是一个自动化模块
    环境的安装 pip install selenium
2.selenium的具体使用
 准备浏览器的驱动程序:http://chromedriver.storage.googleapis.com/index.html   
 引用模块from selenium import webdriver	
img

3.代码实现

普通执行点击事件

from selenium import webdriver
from time import sleep

# 后面是你的浏览器驱动位置,记得前面加r'','r'是防止字符转义的
driver = webdriver.Chrome(r'chromedriver')
# 用get打开百度页面
driver.get("http://www.baidu.com")
# 查找页面的“设置”选项,并进行点击
driver.find_elements_by_link_text('设置')[0].click()
sleep(2)
# # 打开设置后找到“搜索设置”选项,设置为每页显示50条
driver.find_elements_by_link_text('搜索设置')[0].click()
sleep(2)

# 选中每页显示50条
m = driver.find_element_by_id('nr')
sleep(2)
m.find_element_by_xpath('//*[@id="nr"]/option[3]').click()
m.find_element_by_xpath('.//option[3]').click()
sleep(2)

# 点击保存设置
driver.find_elements_by_class_name("prefpanelgo")[0].click()
sleep(2)

# 处理弹出的警告页面   确定accept() 和 取消dismiss()
driver.switch_to_alert().accept()
sleep(2)
# 找到百度的输入框,并输入 美女
driver.find_element_by_id('kw').send_keys('美女')
sleep(2)
# 点击搜索按钮
driver.find_element_by_id('su').click()
sleep(2)
# 在打开的页面中找到“Selenium - 开源中国社区”,并打开这个页面
driver.find_elements_by_link_text('美女_百度图片')[0].click()
sleep(3)

# 关闭浏览器
driver.quit()

基本操作使用

from selenium import webdriver#引用一个模块
from time import sleep
#结合着浏览去的驱动实例化一个浏览器对象
bro = webdriver.Chrome(executable_path='./chromedriver.exe')

#请求的发送
url = 'https://www.jd.com/'
bro.get(url)#获取url路径
sleep(1)
#标签定位
# bro.find_element_by_xpath('//input[@id="key"]')通过xpath语法找到对应标签
search = bro.find_element_by_id('key')#通过id找到对应标签
search.send_keys('mac pro')#向指定标签中录入文本数据
sleep(2)
btn = bro.find_element_by_xpath('//*[@id="search"]/div/div[2]/button')
btn.click()#点击
sleep(2)
#JS注入 执行js
bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')

#捕获到当前页面的数据
page_text = bro.page_source
print(page_text)
sleep(3)

bro.quit()#退出浏览器

可以动态获取数据

和requests模块获取的区别

1.request模块
	#制定一个通用的url模板,不可以被改变
    url = 'http://duanziwang.com/category/搞笑图/%d/'
    #通过for循环获取页面
    for page in range(1,4):
        new_url = format(url%page)
        page_text = requests.get(new_url,headers=headers).text #页面源码数据
2.selenium模块
	#可以通过点击事件直接获取页面 .click()
    for i in range(2):
    nextPage = bro.find_element_by_xpath('//*[@id="pageIto_next"]')
    nextPage.click()
    sleep(1)
    all_page_text.append(bro.page_source)

	for page_text in all_page_text:
    	tree = etree.HTML(page_text)
    	li_list = tree.xpath('//*[@id="gzlist"]/li')
    	for li in li_list:
        	name = li.xpath('./dl/@title')[0]
        	print(name)

4.模拟登陆12306

4.1模拟登陆分析

12306模拟登录分析:
	判断是否是动态变化 使用selenium可以无需关系是否动态因为都可以爬下来
    1.封装一个连接识别的验证码的函数和打码平台对接
    2.通过使用selenium模块模拟浏览器做一系列操作
    3.
    - 验证码的的处理:

具体代码实现

打码平台接口
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
from selenium.webdriver import ActionChains
from time import sleep
from PIL import Image #安装PIL或者是Pillow
from CJY import Chaojiying_Client

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


bro = webdriver.Chrome(executable_path='./chromedriver.exe')

#获取一个网址
bro.get('https://kyfw.12306.cn/otn/login/init')
sleep(2)
#将当前浏览器页面进行图片保存
bro.save_screenshot('./main.png')
#将验证码的局部区域进行裁剪

#捕获标签在页面中的位置信息
img_tag = bro.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
location = img_tag.location#标签的起始位置坐标
size = img_tag.size#标签的尺寸
#裁剪范围对应的矩形区域
rangle = (int(location['x']),int(location['y']),int(location['x']+size['width']),int(location['y']+size['height']))
#使用Image工具进行指定区域的裁剪
i = Image.open('./main.png')#打开图片进行二次裁剪
frame = i.crop(rangle)#crop就是根据指定的裁剪范围进行图片的截取
frame.save('code.png')

#调用打码平台进行验证码的识别
result = transformCode('./code.png',9004)
print(result) #x1,y1|x2,y2|x3,y3

#x1,y1|x2,y2|x3,y3 ==>[[x1,y1],[x2,y2],[x3,y3]]
all_list = []#[[x1,y1],[x2,y2],[x3,y3]]
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)


for point in all_list:
    x = point[0]
    y = point[1]
    ActionChains(bro).move_to_element_with_offset(img_tag,x,y).click().perform()
    sleep(1)

#找到username标签 填写值 进行发送请求
bro.find_element_by_id('username').send_keys('xxxxxx')
sleep(1)
#找到password填写值
bro.find_element_by_id('password').send_keys('xxxx')
sleep(1)

bro.find_element_by_id('loginSub').click()

sleep(10)
#打印获取的页面数据
#捕获到当前页面的数据
print(bro.page_source)
bro.quit()

4.动作链:ActionChains,一系列的行为动作

- 使用流程:
 	实例化一个动作连对象,需要将指定的浏览器和动作连对象进行绑定
 	执行相关的连续的动作
 	perform()立即执行动作连制定好的动作

参数

1.导包:from selenium.webdriver import ActionChains
	创建一个动作链对象:action = ActionChains(bro)
2.行为动作: 
	标签定位:find系列的函数 
	find_element_by_id()#通过对应的id找标签
	find_element_by_xpath()#通过xpath表达式找到对应标签
3.节点交互(操作节点的方法)
	#点击:
    click() 
    #向指定标签中录入文本数据
    send_keys()
    #示例
	search.send_keys('mac pro')#向指定标签中录入文本数据
4. js注入 
	1.执行js
	2.就是让客户端执行的操作,不会影响 服务端 和其他客户端的操作: 
		bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
5.动作链 
	5.1.导包:
        from selenium.webdriver import ActionChains
	5.2.创建一个动作链对象:
        action = ActionChains(bro)
	5.3.调用动作链对象中封装的属性和方法: 
		action.click_and_hold(div_tag)#点击且长按
         #xoffset= x轴移动 y轴移动 yoffset=
		action.move_by_offset(xoffset=15,yoffset=15).perform()#移动并执行
		move_by_offset(x,y)#
		perform():立即执行动作链
		release()在某个元素位置松开鼠标左键
		page_source:返回当前浏览器显示页面的全部页面源码数据
  

使用流程(代码实现):

from selenium import webdriver
from selenium.webdriver import ActionChains#动作连
from time import sleep
bro = webdriver.Chrome(executable_path='./chromedriver.exe')

url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'

bro.get(url)
#NoSuchElementException:定位的标签是存在与iframe之中,则就会抛出这个错误
#解决方法:switch_to.frame进行指定子页面的切换
bro.switch_to.frame('iframeResult')
div_tag = bro.find_element_by_xpath('//*[@id="draggable"]')#使用xpath定位标签

#实例化一个动作连对象
action = ActionChains(bro)
action.click_and_hold(div_tag)#点击且长按

#perform()让动作连立即执行
for i in range(5):
    action.move_by_offset(xoffset=15,yoffset=15).perform()
    sleep(2)
action.release()
sleep(5)
bro.quit()

5.selenium规避风险

  • 正经打开一个网站进行window.navigator.webdriver的js注入,返回值为undefined
  • 使用selenium打开的页面,进行上述js注入返回的是true
#规避检测
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
#指定浏览器使用规避检测 指定options
bro = webdriver.Chrome(executable_path='./chromedriver.exe',options=option)
url = 'https://www.taobao.com/'
bro.get(url)

3.无头浏览器

执行selenium无需打开浏览器了

- phantomJs
- 谷歌无头 常用谷歌无头

无头浏览器的使用

#无头浏览器
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from time import sleep
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)
url = 'https://www.taobao.com/'
bro.get(url)
sleep(2)
bro.save_screenshot('123.png')

print(bro.page_source)

4.开启这个红点实时爬取包动态数据值

image-20191206091136910

5.js注入

就是让客户端执行的操作,不会影响 服务端 和其他客户端的操作

15.移动端数据的爬取

概念

  • 常用的抓包工具:本质就是一款代理服务器,适用于实现请求和相应的拦截
    • fiddler
      • 默认只可以抓起http协议的请求和相应
      • https:
        • tools-》options-》https-》detrypt HTTPS traffic
          • 含义:将fiddle的证书安装到本地
    • 青花瓷
    • miteproxy

移动端数据的爬取

  • 配置相关的环境

    • fiddler的配置:
      • tools-》options-》connections->allow remote conxxx
      • 查看fiddler的端口号,并且记住端口号
    • 测试配置是否生效:
      • 在电脑的浏览器访问:http://localhost:端口号/,如果可以访问成共则表示配置成功
  • 网络设置:

    • fiddler所在的电脑开启一个热点,手机连接热点
      • 保证手机和电脑同处于同一个网段
  • 在手机中安装fiddler的证书

    • 手机浏览器访问:

      http://电脑的ip:端口号/

      • 点击下载证书的链接,进行证书下载
    • 在手机中新任且安装证书

  • 开启手机网络代理

    • wifi-》点击感叹号-》开启代理-》代理的ip就是电脑的ip,端口就是fiddler的端口
  • appnium是一个基于手机应用自动化的模块

使用fidder

1.获取证书

image-20191206121545284

2.移动端配置

image-20191206122232108

3.查看响应

image-20191206122249605

4.测试是否生效

访问http://localhost:端口号 如果可以访问就说明配置成功

5.网络设置

fiddler所在的电脑开启一个热点,手机连接热点
	-保证手机和电脑处于同一个网段
手机中安装fiddler的证书
	手机浏览器访问:http://电脑的ip和端口号

scrapy--异步框架

scrapy

  • 异步的爬虫框架。
    • 高性能的数据解析,持久化存储,全栈数据的爬取,中间件,分布式
  • 框架:就是一个集成好了各种功能且具有很强通用性的一个项目模板。
  • twisted 帮助srapy实现异步 是基于twisted

1.环境安装:

Linux:

  pip3 install scrapy

Windows:

  a. pip3 install wheel

  b. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
  #对应tsi版本

  c. 进入下载目录,执行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl #路径

  d. pip3 install pywin32

  e. pip3 install scrapy

2.数据解析(参数):

- 1.response.xpath('xpath表达式')
- 2.scrapy中的xpath解析,在进行数据提取的时候,xpath方法返回的列表中存储		的不再是字符串,
  	而是存储的Selector对象,相关的字符串数据是存储在Selector对象的data参数	中,我们必须使用
  	extract()/extract_first()进行字符串数据的提取
	- extract():可以作用到列表中的每一个列表元素中,返回的依然是一个列表
	- extract_first():只可以作用到列表中的第一个列表元素中,返回的是		字符串
3.持久化存储
  - 基于终端指令的持久化存储
    - 只可以将parse方法的返回值存储到指定后缀的文本文件中。
    获取返回值
    - scrapy crawl spiderName -o ./duanzi.csv
scrapy --help

3.基本使用

- 1.新建一个工程:scrapy startporject proName
	在当前目录下创建爬虫工程
  - settings.py:当前工程的配置文件
  - spiders:爬虫包,必须要存放一个或者多个爬虫文件(.py)
- 2.cd proName
- 3.#scrapy genspider 应用名称 爬取网页的起始url 	
	scrapy genspider spiderName www.xxx.com
    '''
    import scrapy
    class SpidernameSpider(scrapy.Spider):
        name = 'spiderName'#对应的spiderName
        allowed_domains = ['www.ppt .com']
        start_urls = ['http://www.xxx.com/']

        def parse(self, response):
            pass

    '''
- 4.执行工程:scrapy crawl spiderName(应用名称)

  5.settings.py:
      - 不遵从robots协议
      - UA伪装
      - 指定日志输出的类型:LOG_LEVEL = 'ERROR'

1.新建工程

image-20191206152141027

settings的设置

不使用robots协议和ua伪装的设置

BOT_NAME = 'firstBlood'

SPIDER_MODULES = ['firstBlood.spiders']

NEWSPIDER_MODULE = 'firstBlood.spiders'

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'# ua伪造

ROBOTSTXT_OBEY = False 不使用robots协议

LOG_LEVEL = 'ERROR'#打印错误

2.执行工程

 2.cd proName项目名
    - 3.scrapy genspider spiderName www.xxx.com
    - 4.执行工程:scrapy crawl spiderName

img

3.项目架构(目录结构解释)

tutorial/
    scrapy.cfg            # 部署配置文件

    tutorial/             # Python模块,代码写在这个目录下
        __init__.py

        items.py          # 项目项定义文件

        pipelines.py      # 项目管道文件

        settings.py       # 项目设置文件

        spiders/          # 我们的爬虫/蜘蛛 目录
            __init__.py

Scrapy写法

参数
1.必须继承 scrapy.Spider

2.name:标识爬虫。它在项目中必须是唯一的,也就是说,您不能为不同的Spider设置相同的名称。

3.start_requests():必须返回一个迭代的Requests(你可以返回请求列表或写一个生成器函数),Spider将开始抓取。后续请求将从这些初始请求连续生成。

4.def parse(self, response):将被调用来处理为每个请求下载的响应的方法。 response参数是一个TextResponse保存页面内容的实例,并且具有更多有用的方法来处理它。
	parse()方法通常解析响应,提取抓取的数据作为词典,并且还找到要跟踪的新网址并从中创建新的请求(Request)
使用
class FirstSpider(scrpy.Spider):
    爬虫名称 当前爬虫文件的唯一标识
    name='first'
    #def parse
返回值response封装好了
内容被封装在了Selector对象中,需要调用extract()函数将解析的内容从Selecor中取出
xpath.extract()取对应的data值
extract()取多个
extract_first()取一个值
执行
进入项目根目录,也就是上面的tutorial目录
cd proName项目名
执行爬虫:
scrapy crawl proName项目名

存入文件只能指定格式

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


class FirstSpider(scrapy.Spider):
    #爬虫名称:当前爬虫文件的唯一标识
    name = 'first'
    #允许的域名
    # allowed_domains = ['www.baidu.com']
    #起始的url列表:列表元素只可以是url
    #作用:列表元素表示的url就会被进行请求发送
    start_urls = ['http://duanziwang.com/category/%E7%BB%8F%E5%85%B8%E6%AE%B5%E5%AD%90/']
    #数据解析
    #调用次数是由请求次数决定
    一次请求一次解析
    # def parse(self, response):
    #     article_list = response.xpath('/html/body/section/div/div/main/article')
    #     for article in article_list:
    #         #xpath在进行数据提取时,返回的不再是字符串而是一个Selector对象,想要的数据被包含在了该对象的data参数中
    #         # title = article.xpath('./div[1]/h1/a/text()')[0].extract()
    #         title = article.xpath('./div[1]/h1/a/text()').extract_first()
    #         content = article.xpath('./div[2]//text()').extract()
    #         content = ''.join(content)
    #         print(title,content)


    #基于终端指令的持久化存储
    def parse(self, response):
        all_data = []
        article_list = response.xpath('/html/body/section/div/div/main/article')
        for article in article_list:
            #xpath在进行数据提取时,返回的不再是字符串而是一个Selector对象,想要的数据被包含在了该对象的data参数中
            # title = article.xpath('./div[1]/h1/a/text()')[0].extract()
            title = article.xpath('./div[1]/h1/a/text()').extract_first()
            content = article.xpath('./div[2]//text()').extract()
            content = ''.join(content)
            dic = {
                'title':title,
                'content':content
            }
            all_data.append(dic)
        return all_data #将解析到的数据进行了返回

image-20191206160827154

爬虫的总结

1.十种反爬机制

- robots #robots防君子不防小人 一种形式上的限制
- UA伪装#基于ua检测的一种反爬机制
- 动态变化的请求参数
- 验证码
- cookie#身份的一种标识一般出现在登陆 限制某种访问权限控制
- 代理#代理服务器
	1.透明 
	2.高匿
- 动态加载的数据
	局部搜索不到
    全局搜索可以搜索的到
    并且是存储在响应头当中
- js加密
- js混淆
- 图片懒加载
posted @ 2019-12-08 23:23  strawberry*  阅读(178)  评论(0)    收藏  举报