目录

动态HTML介绍

JavaScript

jQuery

Ajax

DHTML

Selenium与PlantomJS

Selenium

PhantomJS

案例一:网站模拟登录

案例二:动态页面模拟点击

机器视觉与Tesseract介绍

处理给规范的文字

尝试对验证码进行机器识别处理


爬虫(Spider),反爬虫(Anti-Spider),反反爬虫(Anti-Anti-Spider) 之间恢宏壮阔的斗争...

Day 1

  • 小莫想要某站上所有的电影,写了标准的爬虫(基于HttpClient库),不断地遍历某站的电影列表页面,根据 Html 分析电影名字存进自己的数据库。

  • 这个站点的运维小黎发现某个时间段请求量陡增,分析日志发现都是 IP(xxx.xxx.xxx.xxx)这个用户,并且 user-agent 还是 Python-urllib/2.7 ,基于这两点判断非人类后直接在服务器上封杀。

Day 2

  • 小莫电影只爬了一半,于是也针对性的变换了下策略:1. user-agent 模仿百度("Baiduspider..."),2. IP每爬半个小时就换一个IP代理。

  • 小黎也发现了对应的变化,于是在服务器上设置了一个频率限制,每分钟超过120次请求的再屏蔽IP。 同时考虑到百度家的爬虫有可能会被误伤,想想市场部门每月几十万的投放,于是写了个脚本,通过 hostname 检查下这个 ip 是不是真的百度家的,对这些 ip 设置一个白名单。

Day 3

  • 小莫发现了新的限制后,想着我也不急着要这些数据,留给服务器慢慢爬吧,于是修改了代码,随机1-3秒爬一次,爬10次休息10秒,每天只在8-12,18-20点爬,隔几天还休息一下。

  • 小黎看着新的日志头都大了,再设定规则不小心会误伤真实用户,于是准备换了一个思路,当3个小时的总请求超过50次的时候弹出一个验证码弹框,没有准确正确输入的话就把 IP 记录进黑名单。

Day 4

  • 小莫看到验证码有些傻脸了,不过也不是没有办法,先去学习了图像识别(关键词 PIL,tesseract),再对验证码进行了二值化,分词,模式训练之后,总之最后识别了小黎的验证码(关于验证码,验证码的识别,验证码的反识别也是一个恢弘壮丽的斗争史...),之后爬虫又跑了起来。

  • 小黎是个不折不挠的好同学,看到验证码被攻破后,和开发同学商量了变化下开发模式,数据并不再直接渲染,而是由前端同学异步获取,并且通过 JavaScript 的加密库生成动态的 token,同时加密库再进行混淆(比较重要的步骤的确有网站这样做,参见淘宝和微博的登陆流程)。

Day 5

  • 混淆过的加密库就没有办法了么?当然不是,可以慢慢调试,找到加密原理,不过小莫不准备用这么耗时耗力的方法,他放弃了基于 HttpClient的爬虫,选择了内置浏览器引擎的爬虫(关键词:PhantomJS,Selenium),在浏览器引擎运行页面,直接获取了正确的结果,又一次拿到了对方的数据。

  • 小黎:.....

爬虫与发爬虫的斗争还在继续...

通常情况下,在爬虫与反爬虫的对弈中,爬虫一定会胜利。

换言之,只要人类能够正常访问的网页,爬虫在具备同等资源的情况下就一定可以抓取到。

关于爬虫部分一些建议:

  1. 尽量减少请求次数,能抓列表页就不抓详情页,减轻服务器压力,程序员都是混口饭吃不容易。

  2. 不要只看 Web 网站,还有手机 App 和 H5,这样的反爬虫措施一般比较少。

  3. 实际应用时候,一般防守方做到根据 IP 限制频次就结束了,除非很核心的数据,不会再进行更多的验证,毕竟成本的问题会考虑到。

  4. 如果真的对性能要求很高,可以考虑多线程(一些成熟的框架如 Scrapy都已支持),甚至分布式...

关于反爬虫部分的一些建议:

  • 这篇文章就够了:携程技术中心 - 携程酒店研发部研发经理崔广宇 <爬虫与反爬虫> 技术分享

动态HTML介绍

JavaScript

JavaScript 是网络上最常用也是支持者最多的客户端脚本语言。它可以收集 用户的跟踪数据,不需要重载页面直接提交表单,在页面嵌入多媒体文件,甚至运行网页游戏。

我们可以在网页源代码的<scripy>标签里看到,比如:

<script type="text/javascript" src="https://statics.huxiu.com/w/mini/static_2015/js/sea.js?v=201601150944"></script>

jQuery

jQuery 是一个十分常见的库,70% 最流行的网站(约 200 万)和约 30% 的其他网站(约 2 亿)都在使用。一个网站使用 jQuery 的特征,就是源代码里包含了 jQuery 入口,比如:

<script type="text/javascript" src="https://statics.huxiu.com/w/mini/static_2015/js/jquery-1.11.1.min.js?v=201512181512"></script>

如果你在一个网站上看到了 jQuery,那么采集这个网站数据的时候要格外小心。jQuery 可 以动态地创建 HTML 内容,只有在 JavaScript 代码执行之后才会显示。如果你用传统的方 法采集页面内容,就只能获得 JavaScript 代码执行之前页面上的内容。

Ajax

我们与网站服务器通信的唯一方式,就是发出 HTTP 请求获取新页面。如果提交表单之后,或从服务器获取信息之后,网站的页面不需要重新刷新,那么你访问的网站就在用Ajax 技术。

Ajax 其实并不是一门语言,而是用来完成网络任务(可以认为 它与网络数据采集差不多)的一系列技术。Ajax 全称是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML),网站不需要使用单独的页面请求就可以和网络服务器进行交互 (收发信息)。

DHTML

Ajax 一样,动态 HTML(Dynamic HTML, DHTML)也是一系列用于解决网络问题的 技术集合。DHTML 是用客户端语言改变页面的 HTML 元素(HTML、CSS,或者二者皆 被改变)。比如页面上的按钮只有当用户移动鼠标之后才出现,背景色可能每次点击都会改变,或者用一个 Ajax 请求触发页面加载一段新内容,网页是否属于DHTML,关键要看有没有用 JavaScript 控制 HTML 和 CSS 元素。

那么,如何搞定?

那些使用了 Ajax 或 DHTML 技术改变 / 加载内容的页面,可能有一些采集手段。但是用 Python 解决这个问题只有两种途径:

  1. 直接从 JavaScript 代码里采集内容(费时费力)
  2. 用 Python 的 第三方库运行 JavaScript,直接采集你在浏览器里看到的页面(这个可以有)。

Selenium与PlantomJS

Selenium

Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动操作,不同是Selenium 可以直接运行在浏览器上,它支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器)。

Selenium 可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。

Selenium 自己不带浏览器,不支持浏览器的功能,它需要与第三方浏览器结合在一起才能使用。但是我们有时候需要让它内嵌在代码中运行,所以我们可以用一个叫 PhantomJS 的工具代替真实的浏览器。

可以从 PyPI 网站下载 Selenium库https://pypi.python.org/simple/selenium ,也可以用 第三方管理器 pip用命令安装:sudo pip install selenium

Selenium 官方参考文档:http://selenium-python.readthedocs.io/index.html

PhantomJS

PhantomJS 是一个基于Webkit的“无界面”(headless)浏览器,它会把网站加载到内存并执行页面上的 JavaScript,因为不会展示图形界面,所以运行起来比完整的浏览器要高效。

如果我们把 Selenium 和 PhantomJS 结合在一起,就可以运行一个非常强大的网络爬虫了,这个爬虫可以处理 JavaScrip、Cookie、headers,以及任何我们真实用户需要做的事情。

PhantomJS 是一个功能完善(虽然无界面)的浏览器而非一个 Python 库,所以它不需要像 Python 的其他库一样安装,但我们可以通过Selenium调用PhantomJS来直接使用。

在Ubuntu16.04中可以使用命令安装:sudo apt-get install phantomjs

如果其他系统无法安装,可以从它的官方网站http://phantomjs.org/download.html) 下载。

PhantomJS 官方参考文档:http://phantomjs.org/documentation

快速入门

Selenium 库里有个叫 WebDriver 的 API。WebDriver 有点儿像可以加载网站的浏览器,但是它也可以像 BeautifulSoup 或者其他 Selector 对象一样用来查找页面元素,与页面上的元素进行交互 (发送文本、点击等),以及执行其他动作来运行网络爬虫。

# IPython2 测试代码# 导入 webdriver
from selenium import webdriver# 调用键盘按键操作时需要引入的Keys包
from selenium.webdriver.common.keys import Keys# 调用环境变量指定的PhantomJS浏览器创建浏览器对象
driver = webdriver.PhantomJS()# 如果没有在环境变量指定PhantomJS位置
# driver = webdriver.PhantomJS(executable_path="./phantomjs"))# get方法会一直等到页面被完全加载,然后才会继续程序,通常测试会在这里选择 time.sleep(2)
driver.get("http://www.baidu.com/")# 获取页面名为 wrapper的id标签的文本内容
data = driver.find_element_by_id("wrapper").text# 打印数据内容
print data# 打印页面标题 "百度一下,你就知道"
print driver.title# 生成当前页面快照并保存
driver.save_screenshot("baidu.png")# id="kw"是百度搜索输入框,输入字符串"长城"
driver.find_element_by_id("kw").send_keys(u"长城")# id="su"是百度搜索按钮,click() 是模拟点击
driver.find_element_by_id("su").click()# 获取新的页面快照
driver.save_screenshot("长城.png")# 打印网页渲染后的源代码
print driver.page_source# 获取当前页面Cookie
print driver.get_cookies()# ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')# ctrl+x 剪切输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')# 输入框重新输入内容
driver.find_element_by_id("kw").send_keys("itcast")# 模拟Enter回车键
driver.find_element_by_id("su").send_keys(Keys.RETURN)# 清除输入框内容
driver.find_element_by_id("kw").clear()# 生成新的页面快照
driver.save_screenshot("itcast.png")# 获取当前url
print driver.current_url# 关闭当前页面,如果只有一个页面,会关闭浏览器
# driver.close()# 关闭浏览器
driver.quit()

页面操作

Selenium 的 WebDriver提供了各种方法来寻找元素,假设下面有一个表单输入框:

<input type="text" name="user-name" id="passwd-id" />

那么:

# 获取id标签值
element = driver.find_element_by_id("passwd-id")
# 获取name标签值
element = driver.find_element_by_name("user-name")
# 获取标签名值
element = driver.find_elements_by_tag_name("input")
# 也可以通过XPath来匹配
element = driver.find_element_by_xpath("//input[@id='passwd-id']")

定位UI元素 (WebElements)

关于元素的选取,有如下的API 单个元素选取

find_element_by_id
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
  1. By ID

    <div id="coolestWidgetEvah">...</div>
    
    • 实现

      element = driver.find_element_by_id("coolestWidgetEvah")
      ------------------------ or -------------------------
      from selenium.webdriver.common.by import By
      element = driver.find_element(by=By.ID, value="coolestWidgetEvah")
      
  1. By Class Name

    <div class="cheese"><span>Cheddar</span></div><div class="cheese"><span>Gouda</span></div>
    
    • 实现

      cheeses = driver.find_elements_by_class_name("cheese")
      ------------------------ or -------------------------
      from selenium.webdriver.common.by import By
      cheeses = driver.find_elements(By.CLASS_NAME, "cheese")
      
  2. By Tag Name

    <iframe src="..."></iframe>
    
    • 实现

      frame = driver.find_element_by_tag_name("iframe")
      ------------------------ or -------------------------
      from selenium.webdriver.common.by import By
      frame = driver.find_element(By.TAG_NAME, "iframe")
      
  3. By Name

    <input name="cheese" type="text"/>
    
    • 实现

      cheese = driver.find_element_by_name("cheese")
      ------------------------ or -------------------------
      from selenium.webdriver.common.by import By
      cheese = driver.find_element(By.NAME, "cheese")
      
  4. By Link Text

    <a href="http://www.google.com/search?q=cheese">cheese</a>
    
    • 实现

      cheese = driver.find_element_by_link_text("cheese")
      ------------------------ or -------------------------
      from selenium.webdriver.common.by import By
      cheese = driver.find_element(By.LINK_TEXT, "cheese")
      
  5. By Partial Link Text

    <a href="http://www.google.com/search?q=cheese">search for cheese</a>>
    
    • 实现

      cheese = driver.find_element_by_partial_link_text("cheese")
      ------------------------ or -------------------------
      from selenium.webdriver.common.by import By
      cheese = driver.find_element(By.PARTIAL_LINK_TEXT, "cheese")
      
  6. By CSS

    <div id="food"><span class="dairy">milk</span><span class="dairy aged">cheese</span></div>
    
    • 实现

      cheese = driver.find_element_by_css_selector("#food span.dairy.aged")
      ------------------------ or -------------------------
      from selenium.webdriver.common.by import By
      cheese = driver.find_element(By.CSS_SELECTOR, "#food span.dairy.aged")
      
  7. By XPath

    <input type="text" name="example" />
    <INPUT type="text" name="other" />
    
    • 实现

      inputs = driver.find_elements_by_xpath("//input")
      ------------------------ or -------------------------
      from selenium.webdriver.common.by import By
      inputs = driver.find_elements(By.XPATH, "//input")
      

鼠标动作链

有些时候,我们需要再页面上模拟一些鼠标操作,比如双击、右击、拖拽甚至按住不动等,我们可以通过导入 ActionChains 类来做到:

示例:


#导入 ActionChains 类
from selenium.webdriver import ActionChains# 鼠标移动到 ac 位置
ac = driver.find_element_by_xpath('element')
ActionChains(driver).move_to_element(ac).perform()# 在 ac 位置单击
ac = driver.find_element_by_xpath("elementA")
ActionChains(driver).move_to_element(ac).click(ac).perform()# 在 ac 位置双击
ac = driver.find_element_by_xpath("elementB")
ActionChains(driver).move_to_element(ac).double_click(ac).perform()# 在 ac 位置右击
ac = driver.find_element_by_xpath("elementC")
ActionChains(driver).move_to_element(ac).context_click(ac).perform()# 在 ac 位置左键单击hold住
ac = driver.find_element_by_xpath('elementF')
ActionChains(driver).move_to_element(ac).click_and_hold(ac).perform()# 将 ac1 拖拽到 ac2 位置
ac1 = driver.find_element_by_xpath('elementD')
ac2 = driver.find_element_by_xpath('elementE')
ActionChains(driver).drag_and_drop(ac1, ac2).perform()

填充表单

我们已经知道了怎样向文本框中输入文字,但是有时候我们会碰到<select> </select>标签的下拉框。直接点击下拉框中的选项不一定可行。

<select id="status" class="form-control valid" onchange="" name="status"><option value=""></option><option value="0">未审核</option><option value="1">初审通过</option><option value="2">复审通过</option><option value="3">审核不通过</option>
</select>


Selenium专门提供了Select类来处理下拉框。 其实 WebDriver 中提供了一个叫 Select 的方法,可以帮助我们完成这些事情:

# 导入 Select 类
from selenium.webdriver.support.ui import Select# 找到 name 的选项卡
select = Select(driver.find_element_by_name('status'))#
select.select_by_index(1)
select.select_by_value("0")
select.select_by_visible_text(u"未审核")

以上是三种选择下拉框的方式,它可以根据索引来选择,可以根据值来选择,可以根据文字来选择。注意:

  • index 索引从 0 开始
  • value是option标签的一个属性值,并不是显示在下拉框中的值
  • visible_text是在option标签文本的值,是显示在下拉框的值

全部取消选择怎么办呢?很简单:

select.deselect_all()

弹窗处理

当你触发了某个事件之后,页面出现了弹窗提示,处理这个提示或者获取提示信息方法如下:

alert = driver.switch_to_alert()

页面切换

一个浏览器肯定会有很多窗口,所以我们肯定要有方法来实现窗口的切换。切换窗口的方法如下:

driver.switch_to.window("this is window name")

也可以使用 window_handles 方法来获取每个窗口的操作对象。例如:

for handle in driver.window_handles:driver.switch_to_window(handle)

页面前进和后退

操作页面的前进和后退功能:

driver.forward()     #前进
driver.back()        # 后退

Cookies

获取页面每个Cookies值,用法如下

for cookie in driver.get_cookies():print "%s -> %s" % (cookie['name'], cookie['value'])

删除Cookies,用法如下

# By name
driver.delete_cookie("CookieName")# all
driver.delete_all_cookies()

页面等待

注意:这是非常重要的一部分!!

现在的网页越来越多采用了 Ajax 技术,这样程序便不能确定何时某个元素完全加载出来了。如果实际页面等待时间过长导致某个dom元素还没出来,但是你的代码直接使用了这个WebElement,那么就会抛出NullPointer的异常。

为了避免这种元素定位困难而且会提高产生 ElementNotVisibleException 的概率。所以 Selenium 提供了两种等待方式,一种是隐式等待,一种是显式等待。

隐式等待是等待特定的时间,显式等待是指定某一条件直到这个条件成立时继续执行。

显式等待

显式等待指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常了。

from selenium import webdriver
from selenium.webdriver.common.by import By
# WebDriverWait 库,负责循环等待
from selenium.webdriver.support.ui import WebDriverWait
# expected_conditions 类,负责条件出发
from selenium.webdriver.support import expected_conditions as ECdriver = webdriver.Chrome()
driver.get("http://www.xxxxx.com/loading")
try:# 页面一直循环,直到 id="myDynamicElement" 出现element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "myDynamicElement")))
finally:driver.quit()

如果不写参数,程序默认会 0.5s 调用一次来查看元素是否已经生成,如果本来元素就是存在的,那么会立即返回。

下面是一些内置的等待条件,你可以直接调用这些条件,而不用自己写某些等待条件了。

title_is
title_contains
presence_of_element_located
visibility_of_element_located
visibility_of
presence_of_all_elements_located
text_to_be_present_in_element
text_to_be_present_in_element_value
frame_to_be_available_and_switch_to_it
invisibility_of_element_located
element_to_be_clickable – it is Displayed and Enabled.
staleness_of
element_to_be_selected
element_located_to_be_selected
element_selection_state_to_be
element_located_selection_state_to_be
alert_is_present

隐式等待

隐式等待比较简单,就是简单地设置一个等待时间,单位为秒。

from selenium import webdriverdriver = webdriver.Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://www.xxxxx.com/loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")

当然如果不设置,默认等待时间为0。

案例一:网站模拟登录

# -*- coding:utf-8 -*-# douban.py
#coding=utf-8
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keysclass Douban():def __init__(self):self.url = "https://www.douban.com/"self.driver = webdriver.PhantomJS()def log_in(self):self.driver.get(self.url)time.sleep(3)#睡3分钟,等待页面加载self.driver.save_screenshot("0.jpg")#输入账号self.driver.find_element_by_xpath('//*[@id="form_email"]').send_keys("xxxxx@qq.com")#输入密码self.driver.find_element_by_xpath('//*[@id="form_password"]').send_keys("xxxx")#点击登陆self.driver.find_element_by_class_name("bn-submit").click()time.sleep(2)self.driver.save_screenshot("douban.jpg")#输出登陆之后的cookiesprint(self.driver.get_cookies())def __del__(self):'''调用内建的稀构方法,在程序退出的时候自动调用类似的还可以在文件打开的时候调用close,数据库链接的断开'''self.driver.quit()if __name__ == "__main__":douban = Douban() #实例化douban.log_in() #之后调用登陆方法

案例二:动态页面模拟点击

爬取斗鱼直播平台的所有房间信息:

#coding=utf-8
from selenium import webdriver
import json
import time
class Douyu:# 1.发送首页的请求def __init__(self):self.driver = webdriver.PhantomJS()self.driver.get("https://www.douyu.com/directory/all") #请求首页#获取没页面内容def get_content(self):time.sleep(3) #每次发送完请求等待三秒,等待页面加载完成li_list = self.driver.find_elements_by_xpath('//ul[@id="live-list-contentbox"]/li')contents = []for i in li_list: #遍历房间列表item = {}item["img"] = i.find_element_by_xpath("./a//img").get_attribute("src") #获取房间图片item["title"] = i.find_element_by_xpath("./a").get_attribute("title") #获取房间名字item["category"] = i.find_element_by_xpath("./a/div[@class='mes']/div/span").text #获取房间分类item["name"] = i.find_element_by_xpath("./a/div[@class='mes']/p/span[1]").text #获取主播名字item["watch_num"] = i.find_element_by_xpath("./a/div[@class='mes']/p/span[2]").text #获取观看人数print(item)contents.append(item)return contents#保存本地def save_content(self,contents):f = open("douyu.txt","a")for content in contents:json.dump(content,f,ensure_ascii=False,indent=2)f.write("\n")f.close()def run(self):#1.发送首页的请求#2.获取第一页的信息contents = self.get_content()#保存内容self.save_content(contents)#3.循环  点击下一页按钮,知道下一页对应的class名字不再是"shark-pager-next"while self.driver.find_element_by_class_name("shark-pager-next"): #判断有没有下一页#点击下一页的按钮self.driver.find_element_by_class_name("shark-pager-next").click() ## 4.继续获取下一页的内容contents = self.get_content()#4.1.保存内容self.save_content(contents)if __name__ == "__main__":douyu = Douyu()douyu.run()

机器视觉与Tesseract介绍

机器视觉

从 Google 的无人驾驶汽车到可以识别假钞的自动售卖机,机器视觉一直都是一个应用广 泛且具有深远的影响和雄伟的愿景的领域。

我们将重点介绍机器视觉的一个分支:文字识别,介绍如何用一些 Python库来识别和使用在线图片中的文字。

我们可以很轻松的阅读图片里的文字,但是机器阅读这些图片就会非常困难,利用这种人类用户可以正常读取但是大多数机器人都没法读取的图片,验证码 (CAPTCHA)就出现了。验证码读取的难易程度也大不相同,有些验证码比其他的更加难读。

将图像翻译成文字一般被称为光学文字识别(Optical Character Recognition, OCR)。可以实现OCR的底层库并不多,目前很多库都是使用共同的几个底层 OCR 库,或者是在上面 进行定制。

ORC库概述

在读取和处理图像、图像相关的机器学习以及创建图像等任务中,Python 一直都是非常出色的语言。虽然有很多库可以进行图像处理,但在这里我们只重点介绍:Tesseract

Tesseract

Tesseract 是一个 OCR 库,目前由 Google 赞助(Google 也是一家以 OCR 和机器学习技术闻名于世的公司)。Tesseract 是目前公认最优秀、最精确的开源 OCR 系统,除了极高的精确度,Tesseract 也具有很高的灵活性。它可以通过训练识别出任何字体,也可以识别出任何 Unicode 字符。

安装Tesseract

Windows 系统

下载可执行安装文件https://code.google.com/p/tesseract-ocr/downloads/list安装。

Linux 系统

可以通过 apt-get 安装: $sudo apt-get tesseract-ocr

Mac OS X系统

用 Homebrew(http://brew.sh/)等第三方库可以很方便地安装 brew install tesseract

安装pytesseract

Tesseract 是一个 Python 的命令行工具,不是通过 import 语句导入的库。安装之后,要用 tesseract 命令在 Python 的外面运行,但我们可以通过 pip 安装支持Python 版本的 Tesseract库:

pip install pytesseract

处理给规范的文字

处理的大多数文字最好都是比较干净、格式规范的。格式规范的文字通常可以满足一些需求,通常格式规范的文字具有以下特点:

  • 使用一个标准字体(不包含手写体、草书,或者十分“花哨的”字体)
  • 即使被复印或拍照,字体还是很清晰,没有多余的痕迹或污点
  • 排列整齐,没有歪歪斜斜的字
  • 没有超出图片范围,也没有残缺不全,或紧紧贴在图片的边缘

文字的一些格式问题在图片预处理时可以进行解决。例如,可以把图片转换成灰度图,调整亮度和对比度,还可以根据需要进行裁剪和旋转(详情需要了解图像与信号处理)等。

格式规范文字的理想示例

通过下面的命令运行 Tesseract,读取文件并把结果写到一个文本文件中: tesseract test.jpg text

cat text.txt 即可显示结果。

识别结果很准确,不过符号^*分别被表示成了双引号和单引号。大体上可以让你很舒服地阅读。

通过Python代码实现

import pytesseract
from PIL import Imageimage = Image.open('test.jpg')
text = pytesseract.image_to_string(image)
print text

运行结果:

This is some text, written in Arial, that will be read by
Tesseract. Here are some symbols: !@#$%"&*()

对图片进行阈值过滤和降噪处理(了解即可)

很多时候我们在网上会看到这样的图片:

Tesseract 不能完整处理这个图片,主要是因为图片背景色是渐变的,最终结果是这样:

随着背景色从左到右不断加深,文字变得越来越难以识别,Tesseract 识别出的 每一行的最后几个字符都是错的。

遇到这类问题,可以先用 Python 脚本对图片进行清理。利用 PIL 库,我们可以创建一个阈值过滤器来去掉渐变的背景色,只把文字留下来,从而让图片更加清晰,便于 Tesseract 读取:

from PIL import Image
import subprocessdef cleanFile(filePath, newFilePath):image = Image.open(filePath)# 对图片进行阈值过滤(低于143的置为黑色,否则为白色)image = image.point(lambda x: 0 if x < 143 else 255)# 重新保存图片image.save(newFilePath)# 调用系统的tesseract命令对图片进行OCR识别     subprocess.call(["tesseract", newFilePath, "output"])# 打开文件读取结果with open("output.txt", 'r') as f:print(f.read())if __name__ == "__main__":cleanFile("text2.png", "text2clean.png")

通过一个阈值对前面的“模糊”图片进行过滤的结果

除了一些标点符号不太清晰或丢失了,大部分文字都被读出来了。Tesseract 给出了最好的 结果:

从网站图片中抓取文字

用 Tesseract 读取硬盘里图片上的文字,可能不怎么令人兴奋,但当我们把它和网络爬虫组合使用时,就能成为一个强大的工具。

网站上的图片可能并不是故意把文字做得很花哨 (就像餐馆菜单的 JPG 图片上的艺术字),但它们上面的文字对网络爬虫来说就是隐藏起来 了,举个例子:

  • 虽然亚马逊的 robots.txt 文件允许抓取网站的产品页面,但是图书的预览页通常不让网络机 器人采集。

  • 图书的预览页是通过用户触发 Ajax 脚本进行加载的,预览图片隐藏在 div 节点 下面;其实,普通的访问者会觉得它们看起来更像是一个 Flash 动画,而不是一个图片文 件。当然,即使我们能获得图片,要把它们读成文字也没那么简单。

  • 下面的程序就解决了这个问题:首先导航到托尔斯泰的《战争与和平》的大字号印刷版 1, 打开阅读器,收集图片的 URL 链接,然后下载图片,识别图片,最后打印每个图片的文 字。因为这个程序很复杂,利用了前面几章的多个程序片段,所以我增加了一些注释以让 每段代码的目的更加清晰:


import time
from urllib.request import urlretrieve
import subprocess
from selenium import webdriver
#创建新的Selenium driver
driver = webdriver.PhantomJS()# 用Selenium试试Firefox浏览器:
# driver = webdriver.Firefox()driver.get("http://www.amazon.com/War-Peace-Leo-Nikolayevich-Tolstoy/dp/1427030200")
# 单击图书预览按钮 driver.find_element_by_id("sitbLogoImg").click() imageList = set()
# 等待页面加载完成
time.sleep(5)
# 当向右箭头可以点击时,开始翻页
while "pointer" in driver.find_element_by_id("sitbReaderRightPageTurner").get_attribute("style"):driver.find_element_by_id("sitbReaderRightPageTurner").click()time.sleep(2)# 获取已加载的新页面(一次可以加载多个页面,但是重复的页面不能加载到集合中)pages = driver.find_elements_by_xpath("//div[@class='pageImage']/div/img")for page in pages:image = page.get_attribute("src")imageList.add(image)
driver.quit()# 用Tesseract处理我们收集的图片URL链接
for image in sorted(imageList):# 保存图片urlretrieve(image, "page.jpg")p = subprocess.Popen(["tesseract", "page.jpg", "page"], stdout=subprocess.PIPE,stderr=subprocess.PIPE)f = open("page.txt", "r")p.wait() print(f.read())

和我们前面使用 Tesseract 读取的效果一样,这个程序也会完美地打印书中很多长长的段落,第六页的预览如下所示:

6"A word of friendly advice, moncher. Be off as soon as you can,that's all I have to tell you. Happyhe who has ears to hear. Good-by,my dear fellow. Oh, by the by!" heshouted through the doorway afterPierre, "is it true that the countesshas fallen into the clutches of theholy fathers of the Society of je-sus?"Pierre did not answer and left Ros-topchin's room more sullen and an-gry than he had ever before shownhimself.

但是当文字出现在彩色封面上时,结果就不那么完美了:

   WEI' nrrd PeaceLen Nlkelayevldu IolfluyReadmg shmdd be axwlnvame asnossxble Wenfleran mm m our cram: Llhvary- Leo Tmsloy was a Russian rwovelwstI and moval phflmopher med lurA ms Ideas 01 nonviolenx reswslance m 5 We range     0, "and"

如果想把文字加工成普通人可以看懂的效果,还需要花很多时间去处理。

比如,通过给 Tesseract 提供大量已知的文字与图片映射集,经过训练 Tesseract 就可以“学会”识别同一种字体,而且可以达到极高的精确率和准确率,甚至可以忽略图片中文字的背景色和相对位置等问题。

尝试对验证码进行机器识别处理

尝试对知乎网验证码进行处理:

许多流行的内容管理系统即使加了验证码模块,其众所周知的注册页面也经常会遭到网络 机器人的垃圾注册。

那么,这些网络机器人究,竟是怎么做的呢?既然我们已经,可以成功地识别出保存在电脑上 的验证码了,那么如何才能实现一个全能的网络机器人呢?

大多数网站生成的验证码图片都具有以下属性。

  • 它们是服务器端的程序动态生成的图片。验证码图片的 src 属性可能和普通图片不太一 样,比如 <img src="WebForm.aspx?id=8AP85CQKE9TJ">,但是可以和其他图片一样进行 下载和处理。
  • 图片的答案存储在服务器端的数据库里。
  • 很多验证码都有时间限制,如果你太长时间没解决就会失效。
  • 常用的处理方法就是,首先把验证码图片下载到硬盘里,清理干净,然后用 Tesseract 处理 图片,最后返回符合网站要求的识别结果。
#!/usr/bin/env python
# -*- coding:utf-8 -*-import requests
import time
import pytesseract
from PIL import Image
from bs4 import BeautifulSoupdef captcha(data):with open('captcha.jpg','wb') as fp:fp.write(data)time.sleep(1)image = Image.open("captcha.jpg")text = pytesseract.image_to_string(image)print "机器识别后的验证码为:" + textcommand = raw_input("请输入Y表示同意使用,按其他键自行重新输入:")if (command == "Y" or command == "y"):return textelse:return raw_input('输入验证码:')def zhihuLogin(username,password):# 构建一个保存Cookie值的session对象sessiona = requests.Session()headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0'}# 先获取页面信息,找到需要POST的数据(并且已记录当前页面的Cookie)html = sessiona.get('https://www.zhihu.com/#signin', headers=headers).content# 找到 name 属性值为 _xsrf 的input标签,取出value里的值_xsrf = BeautifulSoup(html ,'lxml').find('input', attrs={'name':'_xsrf'}).get('value')# 取出验证码,r后面的值是Unix时间戳,time.time()captcha_url = 'https://www.zhihu.com/captcha.gif?r=%d&type=login' % (time.time() * 1000)response = sessiona.get(captcha_url, headers = headers)data = {"_xsrf":_xsrf,"email":username,"password":password,"remember_me":True,"captcha": captcha(response.content)}response = sessiona.post('https://www.zhihu.com/login/email', data = data, headers=headers)print response.textresponse = sessiona.get('https://www.zhihu.com/people/maozhaojun/activities', headers=headers)print response.textif __name__ == "__main__":zhihuLogin('xxxx@qq.com','ALAxxxxIME')

尝试处理中文字符

如果手头上有中文的训练数据,也可以尝试对中文进行识别。

命令:tesseract --list-langs可以查看当前支持的语言,chi_sim表示支持简体中文。

那么在使候,可以指定某个语言来进行识别,如:

tesseract -l chi_sim paixu.png paixu

表现在程序里,则可以这么写:

#!/usr/bin/env python
# -*- coding:utf-8 -*-from PIL import Image
import subprocessdef cleanFile(filePath)image = Image.open(filePath)# 调用系统的tesseract命令, 对图片进行OCR中文识别subprocess.call(["tesseract", "-l", "chi_sim", filePath, "paixu"])# 打开文件读取结果with open("paixu.txt", 'r') as f:print(f.read())if __name__ == "__main__":cleanFile("paixu.png")

结果如下:

案例三:执行 JavaScript 语句

    1. 隐藏百度图片

from selenium import webdriverdriver = webdriver.PhantomJS()
driver.get("https://www.baidu.com/")# 给搜索输入框标红的javascript脚本
js = "var q=document.getElementById(\"kw\");q.style.border=\"2px solid red\";"# 调用给搜索输入框标红js脚本
driver.execute_script(js)#查看页面快照
driver.save_screenshot("redbaidu.png")#js隐藏元素,将获取的图片元素隐藏
img = driver.find_element_by_xpath("//*[@id='lg']/img")
driver.execute_script('$(arguments[0]).fadeOut()',img)# 向下滚动到页面底部
driver.execute_script("$('.scroll_top').click(function(){$('html,body').animate({scrollTop: '0px'}, 800);});")#查看页面快照
driver.save_screenshot("nullbaidu.png")driver.quit()
    1. 模拟滚动条滚动到底部
#
from selenium import webdriver
import timedriver = webdriver.PhantomJS()
driver.get("https://movie.douban.com/typerank?type_name=剧情&type=11&interval_id=100:90&action=")# 向下滚动10000像素
js = "document.body.scrollTop=10000"
#js="var q=document.documentElement.scrollTop=10000"
time.sleep(3)#查看页面快照
driver.save_screenshot("douban.png")# 执行JS语句
driver.execute_script(js)
time.sleep(10)#查看页面快照
driver.save_screenshot("newdouban.png")driver.quit()

Python学习笔记——爬虫之动态HTML处理和机器图像识别相关推荐

  1. Python学习笔记——爬虫之Scrapy框架

    目录 Scrapy 框架 Scrapy的安装介绍 Windows 安装方式 Ubuntu 需要9.10或以上版本安装方式 入门案例 启动Scrapy Shell Item Pipeline Spide ...

  2. Python学习笔记——爬虫之urllib数据抓取

    目录 urllib库的基本使用 Get方式 POST方式: 获取AJAX加载的内容 Handler处理器 和 自定义Opener urllib库的基本使用 所谓网页抓取,就是把URL地址中指定的网络资 ...

  3. Python学习笔记——爬虫原理与Requests数据抓取

    目录 为什么要做网络爬虫? 通用爬虫和聚焦爬虫 HTTP和HTTPS 客户端HTTP请求 请求方法 HTTP请求主要分为Get和Post两种方法 常用的请求报头 1. Host (主机和端口号) 2. ...

  4. Python学习笔记 - 爬虫

    爬虫简单来讲就是,让程序通过一些设置好的规则去网页上查找我们想要的内容,我还没有爬过有验证码,登录等一系列需要鉴权的网站.所以这里仅仅记录一下我自己写的最简单的爬虫实现. 找到想要爬取的网页 确定数据 ...

  5. python学习笔记爬虫——爬取智联招聘信息

    目的:自己输入指定地方,指定职业,起始页数,程序会自动打印页面的所有信息. 实现过程:这次程序的代码跟以往的都不一样,这次也是我第一次使用面向对象式编程,并且并不是分析网页代码,分析json字符串得到 ...

  6. python爬虫之b站视频下载(python学习笔记)

    b站视频爬取(python学习笔记) 亲爱的b站自从在2020年3月23日将av号改为BV号后,之前的很多爬虫已经不能用了,暂时没看懂这操作的意义何在,反而一大串大小写英文字母加数字的BV号让人很难记 ...

  7. Python 学习笔记 第三篇 Python实现网易云评论网页爬虫+词云展示 (Pycharm+Mysql)

    初始条件,具体可见我的其他文章. 1.安装Python.Python 学习笔记 第一篇 Python的安装与配置 2.安装Pycharm,并导入第三方包.Python 学习笔记 第二篇 Python ...

  8. PYTHON学习笔记之(一)2020.08

    PYTHON学习笔记之(一)2020.08 Python基础 数据类型 常见的列表.字典,以及元组.集合. 1 列表 list 1.1 列表转换字符串 stu = ['王一', '李二', '张三'] ...

  9. python学习笔记,初学者Python3

    Python学习笔记 不推荐将python作为主力语言,但推荐作为一个辅助语言,用于日常的一些小的任务 python目前主要的应用场景: 1.机器学习,人工智能 2.爬虫 3.数据分析 4.自动化,运 ...

最新文章

  1. 学习Vim有什么好处? [关闭]
  2. 如何从另一个线程更新GUI?
  3. sqlServer事务
  4. C++实现全局鼠标、键盘消息hook,支持事件
  5. 淘宝网秒杀需求分析与实现 - 公开课笔记
  6. oracle asm 异机挂载,oracle 异机恢复 从asm到文件系统成功实例
  7. html input file 置空,清空 HTML File Input
  8. Python实现红黑树的插入操作
  9. RunLoop相关知识
  10. winform 属性
  11. 最常用的Windows快捷键大全【高手都是如何玩电脑的】(不断更新中……)
  12. python矩阵连乘_动态规划之矩阵连乘问题Python实现方法
  13. 三国杀7.21服务器维护,三国杀21日7时2.75版本更新维护公告
  14. Midjourney:一步一步教你如何使用 AI 绘画 MJ
  15. iPhone再添新配色: 黄铜色?
  16. 支付宝面试:什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决?...
  17. WHQL签名是什么?
  18. 适合新手 练手的Java 实战项目
  19. Python界面编程第八课:Pyside2 (Qt For Python)打开关于窗口
  20. 中职计算机技能,中职计算机专业技能竞赛规则

热门文章

  1. Transformer(multi-head attention)
  2. 2018 9 27 随想
  3. 【热血传奇】 添加npc
  4. 7月火影忍者服务器维护,7月2日停机更新公告
  5. 移动支付市场前景如何?让我们用数据讲话。。。
  6. 【附源码】计算机毕业设计java作业批改管理系统设计与实现
  7. 【转】蒙哥马利算法学习
  8. 诺威健康通过聆讯:已派付股息1570万美元,称预计将录得巨额亏损
  9. 辗转取余数php算最小公约数,(算法)求最大公约数和最小公约数
  10. linux给服务器配置ip地址,linux下配置ip地址四种方法(图文方法)