python爬虫入门 之 单线程 + 多任务异步协程

第五章 .单线程 + 多任务异步协程

5.1 进程和线程

  • 自己写一个服务端

    from flask import Flask
    import time

    app = Flask(__name__)

    @app.route('/one')
    def index_one():
       time.sleep(2)
       return  "hello one"

    @app.route('/two')
    def index_two():
       time.sleep(2)
       return  "hello two"

    @app.route('/three')
    def index_three():
       time.sleep(2)
       return  "hello three"

    if __name__ == '__main__':
       app.run(threaded=True)
  • 同步代码

    from multiprocessing.dummy import Pool
    import requests
    import time

    start = time.time()
    pool = Pool(3)

    urls = ["http://127.0.0.1:5000/one","http://127.0.0.1:5000/two","http://127.0.0.1:5000/three"]

    for url in urls:
       page_text = requests.get(url,).text
       print(page_text)
    print('总耗时:',time.time()-start)

  • 异步代码

    from multiprocessing.dummy import Pool
    import requests
    import time

    start = time.time()
    pool = Pool(3)

    urls = ["http://127.0.0.1:5000/one","http://127.0.0.1:5000/two","http://127.0.0.1:5000/three"]

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

    response_list = pool.map(get_request,urls)
    print(response_list)

    #解析
    def parse(page_text):
       print(len(page_text))

    pool.map(parse,response_list)
    print('总耗时:',time.time()-start)
    print('总耗时:',time.time()-start)

5.2 协程

  • asyncio链接https://www.liaoxuefeng.com/wiki/1016959663602400/1017970488768640

  • 是一个对象,可以将协程当做一个特殊的函数,如果一个函数的定义被 anync 关键字所修饰,该特殊函数被调用后,函数内部的程序语句不会立即执行 ,而是返回一个协程对象

    from time import sleep

    async def get_request(url):
       print('正在请求:',url)
       sleep(2)
       print('请求结束')

    c = get_request("www.baidu.com")
    print(c)    # <coroutine object get_request at 0x000000000A184308>

5.3任务对象(task)

  • 所谓的任务对象就是对携程对象的进一步封装,在任务对象中可以实现显示协程对象的运行状况,

  • 特点

    • 任务对象最终是要被注册到事件循环对象中的.

    • 回调函数是绑定给任务对象的,只有当任务对象对应的特殊函数被执行完毕后,回调函数才会被执行

  • await 挂起的操作,可理解为交出cpu的使用权

  • 事件循环对象

    • 无限循环的对象,也可以把其当成是某一种容器,该容器中需要放入多个任务对象(就是一组待执行的代码块)

    • 异步的体现 :当事件循环开始后,该对象会按照顺序执行每一个任务对象,当一个任务对象发生阻塞时不会等待,而是直接执行下一个任务对象

    from time import sleep
    import asyncio

    #回调函数
    def callback(task):
       print("i am callback")
       print(task.result())    #result 返回的就是任务对象对应的哪个特殊函数的返回值


    async def get_request(url):
       print('正在请求:',url)
       sleep(2)
       print('请求结束')
       return "hello world"

    #创建一个携程对象
    c = get_request("www.baidu.com")
    #封装一个任务对象
    task = asyncio.ensure_future(c)

    #给任务对象绑定回调函数
    task.add_done_callback(callback)

    #创建一个事件循环对象
    loop = asyncio.get_event_loop()
    #将任务对象注册到事件循环中并且开启了事件循环
    loop.run_until_complete(task)

5.4多任务异步协程

#注意事项:
1.将多个任务对象存储到一个列表中,然后将该列表注册到事件循环中,在注册的过程中,该列表需要被wait方法进行处理
2.在任务对象对应的特殊函数内部的实现中,不可以出现不支持异步模块的代码,否则就会中断整个的异步效果,并且在该函数的每一阻塞的操作都必须使用await关键字进行修饰
3.requests模块对应的代码不可以出现在特殊函数内部,因为requests 不支持异步操作
import time
import asyncio

start = time.time()
urls = [
   "http://127.0.0.1:5000/one",
   "http://127.0.0.1:5000/one",
   "http://127.0.0.1:5000/two",
   "http://127.0.0.1:5000/three"
]

#待执行的代码块中不可以出现不支持异步模块的代码
#在该函数内部如果有阻塞,阻塞操作必须使用await关键字修饰
async def get_request(url):
   print('正在请求:',url)
   # await不用不会报错,但不会有异步效果
   await asyncio.sleep(2)
   print('请求结束')
   return "hello world"

#放置所有的任务对象
tasks = []
for url in urls:
   c = get_request(url)
   task = asyncio.ensure_future(c)
   tasks.append(task)

loop = asyncio.get_event_loop()
#wait不用会报错
loop.run_until_complete(asyncio.wait(tasks))
print("所用时长:",time.time()-start)

5.4.1在爬虫中使用 多任务异步协程

import time
import asyncio
import requests
start = time.time()
urls = [
   "http://127.0.0.1:5000/one",
   "http://127.0.0.1:5000/two",
   "http://127.0.0.1:5000/three"
]


# 无法实现异步的效果,因为requests 不支持异步操作
async def req(url):
   page_text = await requests.get(url).text
   return page_text


#构建任务列表
tasks = []
for url in urls:
   #协程对象
   c = req(url)
   #任务对象
   task = asyncio.ensure_future(c)
   tasks.append(task)

#创建事件循环对象
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print(time.time()-start)

如上图,我们会发现出现了一个错误,我们尝试了解决,发现:
#无法实现异步的效果,因为requests 不支持异步操作
所以我们引用了了aiohttp模块

5.4.2 aiohttp模块

  • 一个支持异步操作的网络请求的模块

  • 环境的安装 pip install aiohttp

  • 记忆步骤:

    第一步: 初步架构

    async def req(url):
    with aiohttp.ClientSession() as s:
    with s.get(url) as response:
    page_text = response.text()
    return page_text

    第二步: 补充细节

    在每一个 with 之前加上 async,在每一步阻塞前加上 await

    async def req(url):
       async with aiohttp.ClientSession() as s:
           async with await s.get(url) as response:
               page_text = await response.text()
               return page_text

    server端代码

    from flask import Flask, render_template
    import time

    app = Flask(__name__)

    @app.route('/one')
    def index_one():
       time.sleep(2)
       return  render_template("text.html")

    @app.route('/two')
    def index_two():
       time.sleep(2)
       return  "hello two"

    @app.route('/three')
    def index_three():
       time.sleep(2)
       return  "hello three"

    if __name__ == '__main__':
       app.run(threaded=True)

    客户端代码

    import time
    import asyncio
    import aiohttp
    from lxml import etree

    start = time.time()
    urls = [
       "http://127.0.0.1:5000/one",
       "http://127.0.0.1:5000/two",
       "http://127.0.0.1:5000/three"
    ]


    # 无法实现异步的效果,因为requests 不支持异步操作
    async def req(url):
       async with aiohttp.ClientSession() as s:
           async with await s.get(url) as response:
               # response.read() 返回 byte 类型的数据
               # response.text() 返回 字符串
               page_text = await response.text()
               return page_text
       # 细节的补充 : 在每一个 with 之前加上 async,在每一步阻塞前加上 await


    def parse(task):
       page_text = task.result()
       tree = etree.HTML(page_text)
       name = tree.xpath("//p/text()")[0]
       print(name)

    #构建任务列表
    tasks = []
    for url in urls:
       #协程对象
       c = req(url)
       #任务对象
       task = asyncio.ensure_future(c)

       task.add_done_callback(parse)
       tasks.append(task)

    #创建事件循环对象
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    print(time.time()-start)

第六章 .图片懒加载技术、selenium和PhantomJS

6.1图片懒加载

  • 案例分析:抓取站长素材http://sc.chinaz.com/中的图片数据

    import requests
    from lxml import etree

    if __name__ == "__main__":
        url = 'http://sc.chinaz.com/tupian/gudianmeinvtupian.html'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
        }
        #获取页面文本数据
        response = requests.get(url=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:
            image_url = div.xpath('.//img/@src')
            image_name = div.xpath('.//img/@alt')
            print(image_url) #打印图片链接
            print(image_name)#打印图片名称
  • 运行结果观察发现,我们可以获取图片的名称,但是链接获取的为空,检查后发现xpath表达式也没有问题,究其原因出在了哪里呢?

    • 图片懒加载概念:

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

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

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

  • 站长素材案例后续分析:通过细致观察页面的结构后发现,网页中图片的链接是存储在了src2这个伪属性中

    import requests
    from lxml import etree

    if __name__ == "__main__":
        url = 'http://sc.chinaz.com/tupian/gudianmeinvtupian.html'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
        }
        #获取页面文本数据
        response = requests.get(url=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:
            image_url = div.xpath('.//img/@src2') #src2伪属性
            image_name = div.xpath('.//img/@alt')
            print(image_url) #打印图片链接
            print(image_name)#打印图片名称

6.2 selenium

  • 概念: selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题 selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果,可支持多种浏览器

  • 环境的安装 : pip install selenium

  • selenium的演示程序

    from selenium import webdriver
    from time import sleep

    # 后面是你的浏览器驱动位置,记得前面加r'','r'是防止字符转义的
    driver = webdriver.Chrome(r'E:\after homework\day105\chromedriver.exe')
    # 用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()
  • selenium和爬虫之间的关联?

    1.便捷的获取页面中动态加载的数据
    requests模块进行数据爬取,所见非所得
       selenium所见即可得,但速度较慢
    2.实现模拟登陆
  • 基本操作

  • 浏览器创建

    • Selenium支持非常多的浏览器,如Chrome、Firefox、Edge等,还有Android、BlackBerry等手机端的浏览器。另外,也支持无界面浏览器PhantomJS。

      from selenium import webdriver
       
      browser = webdriver.Chrome()
      browser = webdriver.Firefox()
      browser = webdriver.Edge()
      browser = webdriver.PhantomJS()
      browser = webdriver.Safari()
  • 元素定位

    • webdriver 提供了一系列的元素定位方法,常用的有以下几种:

    find_element_by_id()
    find_element_by_name()
    find_element_by_class_name()
    find_element_by_tag_name()
    find_element_by_link_text()
    find_element_by_partial_link_text()
    find_element_by_xpath()
    find_element_by_css_selector()
  • 节点交互

    • Selenium可以驱动浏览器来执行一些操作,也就是说可以让浏览器模拟执行一些动作。比较常见的用法有:输入文字时用send_keys()方法,清空文字时用clear()方法,点击按钮时用click()方法。示例如下:

      from selenium import webdriver
      import time

      browser = webdriver.Chrome()
      browser.get('https://www.taobao.com')
      input = browser.find_element_by_id('q')
      input.send_keys('MAC')
      time.sleep(1)
      input.clear()
      input.send_keys('IPhone')
      button = browser.find_element_by_class_name('btn-search')
      button.click()
      browser.quit()
  • 爬取单页数据

    from selenium import webdriver
    from time import sleep


    #实例化一个浏览器对象
    bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe')  #executable_path 当前浏览器的驱动程序
    url = "https://www.jd.com/"
    #get 用于发起请求
    bro.get(url)
    #定位指定标签
    search_input = bro.find_element_by_id("key")
    #对指定标签进行数据交互
    search_input.send_keys("macPro")

    btn = bro.find_element_by_xpath('//*[@id="search"]/div/div[2]/button')
    btn.click()

    sleep(2)
    #执行js代码
    jsCode = 'window.scrollTo(0,document.body.scrollHeight)'
    bro.execute_script(jsCode)

    sleep(3)
    bro.quit()
  • 爬取多页数据

    from selenium import webdriver
    from time import sleep
    from lxml import etree


    page_text_list = []
    #实例化一个浏览器对象
    bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe')  #executable_path 当前浏览器的驱动程序
    url = "http://125.35.6.84:81/xk/"
    #get 用于发起请求
    bro.get(url)

    sleep(2)
    #page_source就是当前浏览器打开页面对应的源码数据
    page_text = bro.page_source
    page_text_list.append(page_text)

    for i in  range(2):
       bro.find_element_by_id('pageIto_next').click()
       sleep(2)
       page_text = bro.page_source
       page_text_list.append(page_text)


    for page_text in page_text_list:
       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)

    sleep(4)
    bro.quit()
  • 动作链 :一系列的行为动作

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

    from selenium import webdriver
    from selenium.webdriver import ActionChains  #动作链
    from time import sleep


    page_text_list = []
    #实例化一个浏览器对象,executable_path 当前浏览器的驱动程序
    bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe')
    url = "https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable"

    bro.get(url)

    # 如果定位的标签是存在于 iframe 对应的子页面中,在定位标签前一定要进行 switch_to 的操作
    bro.switch_to.frame('iframeResult')   # iframe标签的id
    div_tag = bro.find_element_by_id("draggable")
    # 错误 :selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element:

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

    #模拟人移动算法(自己百度)

    #这里提供一个简单操作
    for i in range(5):
       # perform :让动作链立即执行
       action.move_by_offset(17,0).perform()
       sleep(0.5)

    #释放动作链
    action.release()
    sleep(3)
    bro.quit()
  • 执行JavaScript

    • 对于某些操作,Selenium API并没有提供。比如,下拉进度条,它可以直接模拟运行JavaScript,此时使用execute_script()方法即可实现,代码如下:

    from selenium import webdriver

    browser = webdriver.Chrome()
    browser.get('https://www.jd.com/')
    browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    browser.execute_script('alert("123")')
  • 获取页面源码数据

    • 通过page_source属性可以获取网页的源代码,接着就可以使用解析库(如正则表达式、Beautiful Soup、pyquery等)来提取信息了

  • 前进和后退

    • 模拟浏览器的前进后退

    import time
    from selenium import webdriver

    browser=webdriver.Chrome()
    browser.get('https://www.baidu.com')
    browser.get('https://www.taobao.com')
    browser.get('http://www.sina.com.cn/')

    browser.back()
    time.sleep(10)
    browser.forward()
    browser.close()
  • Cookie处理

    • 使用Selenium,还可以方便地对Cookies进行操作,例如获取、添加、删除Cookies等。示例如下:

      from selenium import webdriver

      browser = webdriver.Chrome()
      browser.get('https://www.zhihu.com/explore')
      print(browser.get_cookies())
      browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'germey'})
      print(browser.get_cookies())
      browser.delete_all_cookies()
      print(browser.get_cookies())
  • 异常处理

    from selenium import webdriver
    from selenium.common.exceptions import TimeoutException,NoSuchElementException,NoSuchFrameException

    try:
       browser=webdriver.Chrome()
       browser.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
       browser.switch_to.frame('iframssseResult')

    except TimeoutException as e:
       print(e)
    except NoSuchFrameException as e:
       print(e)
    finally:
       browser.close()

6.3 phantomJS

  • PhantomJS是一款无界面的浏览器,其自动化操作流程和上述操作谷歌浏览器是一致的。由于是无界面的,为了能够展示自动化操作流程,PhantomJS为用户提供了一个截屏的功能,使用save_screenshot函数实现。

  • 无头浏览器 :无可视化界面的浏览器

    • phantomJS

    谷歌无头浏览器

    from selenium import webdriver
    from time import sleep


    from selenium.webdriver.chrome.options import Options

    # 创建一个参数对象,用来控制chrome以无界面模式打开
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')


    page_text_list = []
    #实例化一个浏览器对象,executable_path 当前浏览器的驱动程序
    bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe',chrome_options=chrome_options)
    bro.get('https://www.baidu.com')

    sleep(2)

    #截图
    bro.save_screenshot("1.png")

    print(bro.page_source)
    sleep(2)
    bro.quit()
  • selenium规避风险

    可判断为正常用户的正常请求

    执行代码时:

    from selenium import webdriver
    from selenium.webdriver import ChromeOptions
    option = ChromeOptions()
    option.add_experimental_option('excludeSwitches', ['enable-automation'])

    #实例化一个浏览器对象
    bro = webdriver.Chrome(executable_path=r'C:\Users\oldboy-python\Desktop\爬虫+数据\day04\chromedriver.exe',options=option)
    bro.get('https://www.taobao.com/')

登录qq空间,爬取数据

import requests
from selenium import webdriver
from lxml import etree
import time

driver = webdriver.Chrome(executable_path='/Users/bobo/Desktop/chromedriver')
driver.get('https://qzone.qq.com/')
#在web 应用中经常会遇到frame 嵌套页面的应用,使用WebDriver 每次只能在一个页面上识别元素,对于frame 嵌套内的页面上的元素,直接定位是定位是定位不到的。这个时候就需要通过switch_to_frame()方法将当前定位的主体切换了frame 里。
driver.switch_to.frame('login_frame')
driver.find_element_by_id('switcher_plogin').click()

#driver.find_element_by_id('u').clear()
driver.find_element_by_id('u').send_keys('328410948')  #这里填写你的QQ号
#driver.find_element_by_id('p').clear()
driver.find_element_by_id('p').send_keys('xxxxxx')  #这里填写你的QQ密码
   
driver.find_element_by_id('login_button').click()
time.sleep(2)
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
page_text = driver.page_source

tree = etree.HTML(page_text)
#执行解析操作
li_list = tree.xpath('//ul[@id="feed_friend_list"]/li')
for li in li_list:
   text_list = li.xpath('.//div[@class="f-info"]//text()|.//div[@class="f-info qz_info_cut"]//text()')
   text = ''.join(text_list)
   print(text+'\n\n\n')
   
driver.close()

爬取豆瓣网中的电影信息

from selenium import webdriver
from time import sleep
import time

if __name__ == '__main__':
   url = 'https://movie.douban.com/typerank?type_name=%E6%81%90%E6%80%96&type=20&interval_id=100:90&action='
   # 发起请求前,可以让url表示的页面动态加载出更多的数据
   path = r'C:\Users\Administrator\Desktop\爬虫授课\day05\ziliao\phantomjs-2.1.1-windows\bin\phantomjs.exe'
   # 创建无界面的浏览器对象
   bro = webdriver.PhantomJS(path)
   # 发起url请求
   bro.get(url)
   time.sleep(3)
   # 截图
   bro.save_screenshot('1.png')

   # 执行js代码(让滚动条向下偏移n个像素(作用:动态加载了更多的电影信息))
   js = 'window.scrollTo(0,document.body.scrollHeight)'
   bro.execute_script(js)  # 该函数可以执行一组字符串形式的js代码
   time.sleep(2)

   bro.execute_script(js)  # 该函数可以执行一组字符串形式的js代码
   time.sleep(2)
   bro.save_screenshot('2.png')
   time.sleep(2)
   # 使用爬虫程序爬去当前url中的内容
   html_source = bro.page_source # 该属性可以获取当前浏览器的当前页的源码(html)
   with open('./source.html', 'w', encoding='utf-8') as fp:
       fp.write(html_source)
   bro.quit()

6.4 基于selenium实现12306模拟登陆

链接地址 :https://kyfw.12306.cn/otn/login/init

# ChaoJiYing.py
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()
# 12306模拟登陆

from selenium import webdriver
from selenium.webdriver import ActionChains
#Image用作于图片裁剪
from PIL import Image
from time import sleep
from ChaoJiYing import  Chaojiying_Client


#实例化一个浏览器对象,executable_path 当前浏览器的驱动程序
bro = webdriver.Chrome(executable_path=r'E:\after homework\day105\chromedriver.exe')
bro.get("https://kyfw.12306.cn/otn/login/init")

sleep(2)
#验证码图片的捕获
bro.save_screenshot("main.png")
#定位验证码图片对应的标签
code_img_ele = bro.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
#验证码图片基于当前整张页面左上角坐标
location = code_img_ele.location
#验证码图片的长和宽
size = code_img_ele.size

#裁剪的矩形区域(左下角和右上角两点的坐标)
rangle = (int(location['x']),int(location['y']),int(location['x']+size['width']),int(location['y']+size['height']))


i = Image.open('main.png')
frame = i.crop(rangle)
frame.save('code.png')

#使用打码平台进行验证码的识别
chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用户中心>>软件ID 生成一个替换 96001
im = open('code.png', 'rb').read() #本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
result = chaojiying.PostPic(im, 9004)['pic_str']
print(result)  # x1,y1|x2,y2|x3,y3 ==> [[x1,y1],[x2,y2],[x3,y3]]
all_list = []#[[x1,y1],[x2,y2],[x3,y3]] 每一个列表元素表示一个点的坐标,坐标对应值的0,0点是验证码图片左下角
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)

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

sleep(3)
bro.quit()
posted @ 2019-09-10 16:04  Primrose  阅读(602)  评论(0编辑  收藏  举报