python爬虫入门 之 单线程 + 多任务异步协程
5.1 进程和线程
- 
自己写一个服务端 from flask import Flask
 import time
 
 app = Flask(__name__)
 
- 
同步代码 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_textserver端代码 from flask import Flask, render_template
 import time
 
 app = Flask(__name__)
 客户端代码 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.实现模拟登陆
- 
基本操作 - 
谷歌浏览器驱动程序下载地址: http://chromedriver.storage.googleapis.com/index.html 
- 
驱动程序和谷歌版本的映射关系表:https://blog.csdn.net/huilan_same/article/details/51896672 #步骤:
 1.实例化某一款浏览器对象(驱动程序的路径)
 2.find系列的函数用于标签定位
 
- 
- 
浏览器创建 - 
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( 
                    
                     
                    
                 
                    
                