欢迎关注哈希大数据微信公众号【哈希大数据】
猫眼电影票房榜单中各电影实时票房数据和总票房数据是经过加密的,咱们直接获取不到,现在咱们就尝试采用图像识别的方法把实时票房数据和总票房数据爬下来,附带把电影主演、上映时间、宣传海报链接等信息也获取下来。
本次爬取主要流程如下:
- 分析网页结构
- 用selenium调用chromedriver去驱动chrome浏览器获取网页信息
- 将票房信息保存为图片,利用PIL对图片进行二值化、锐化等预处理
- 利用tesseract对保存的图片进行识别,得到票房数据
- 保存数据
接下来进行详细说明:
1. 分析网页结构
猫眼电影国内票房榜URL为:https://maoyan.com/board/1,其结构较为简答,只展示国内上线电影实时票房的前十名。
可爬取的字段也只有7个:目前排名、实时票房数据、总票房数据、电影名称、电影主演、上映时间、宣传海报链接,不过我们要多记录一个字段:当前时间,唯一好玩的点在于实时票房是加密的,可以让咱们试试能不能利用图像识别的方式把它搞定。
2. 获取非加密数据
这里使用的是selenium模拟的方式获取网页信息,之前也介绍过,但没用它直接解析数据,前面一般是先把网页信息获取下来,再用BeautifulSoup或PyQuery这两个解析库处理解析信息,今天咱们看看如何用其API直接获取数据:
首先得到所有内容标签:
contents_list = driver.find_elements_by_css_selector('dl.board-wrapper dd')
这里注意咱们用的是find_elements_by_css_selector,element后面加了s。如果不加s,find_element方法查找就只能得到第一个节点,加了s后可获得满足条件的所有节点,以列表类型返回。
对于标签的文本内容的获取也是非常简单,例如提取电影排名:
contents['index']= content.find_element_by_css_selector('i.board-index').text
找到标签后加.text便可获得标签内容。获取标签内属性值也很简单,例如提取电影名称: contents['title']= content.find_element_by_css_selector('img.board-img').get_attribute('alt')
找到标签后加.get_attribute('属性名称')便可获得标签内属性值。具体实例如下:
for content in contents_list:<br> contents['index'] = content.find_element_by_css_selector('i.board-index').text<br>; contents['image_url'] = content.find_element_by_css_selector('img.boardimg').get_attribute('src')<br>;contents['title'] = content.find_element_by_css_selector('img.boardimg').get_attribute('alt')<br> try:<br> contents['actor'] = content.find_element_by_css_selector('p.star').text<br>; except NoSuchElementException:<br>;contents['actor'] = ''<br>; contents['time'] = content.find_element_by_css_selector('p.releasetime').t;
3. 将票房信息保存为图片,利用PIL对图片进行二值化、锐化等预处理操作
将想要获取信息的标签保存图片的一般步骤如下:
a) 截取当前窗口(selenium不能直接截取单个标签)
b) 获取标签位置坐标和标签大小
c) 利用PIL包的crop函数根据标签的坐标和大小将标签剪切保存下来
举例如下:
上图就是网页截图,利用save_screenshot函数可以获得当前窗口截图,咱们需要将蓝色方框内的票房数据剪切保存下来,所以我们需要它们的坐标和大小,幸运的是咱们可以利用.location和.size函数得到标签的坐标和大小,最后再利用PIL包的crop函数根据标签的坐标和大小将标签剪切保存下来。
以上步骤在截取信息在第一屏的情况很好用,但在窗口超过了一屏,且需要截取的元素在后面时,就只能另辟蹊径了。
咱们抓取猫眼电影的电影票房网页就比较长,一屏根本展示不完。当然咱们可以利用js下拉网页然后截图,这样也能将下面网页的信息截取到,但获取到的标签位置坐标的基准点在网页的左上角(如上图提示),crop函数在裁剪时以当前图片左上角为基准,所以基准不同截取出来的内容肯定不对。
在网上查到WebDriver.PhantomJS自带的方法支持对整个网页截屏,这不是太爽了,但当我用的时候又懵逼了,PhantomJS是可以截取整个网页,但截取结果是这样的:
尼玛要啥不给啥呀,同时程序也提示警告selenium已经不再允许使用PhantomJS了,怎么办咱们还是默默回去用chrome吧!这时突然灵光一闪,刚才不能用chrome的原因是location函数获取的标签坐标和crop函数在裁剪坐标基准不同,咱们把它调一致不就可以了(啧啧啧此处应有掌声),说干就干,这个网页上一共有10部电影我只用下拉10次就OK了,所以这里咱们来个for循环,window.scrollTo函数用了下拉网页,它有两个参数第一个是参数代表横向拖动像素数,第二个代表纵向拖动像素数。可是每次下拉多少呢?270px(像素),
咦你咋知道?
来来来告诉大家一个小技巧咱们用微信截图是它会显示截图图片大小,对本实例咱们每次下拉两个电影票房之间的长度就好,如下图其长度为270px。
这样应该是能得到每个票房数据标签了,在得到票房数据标签后咱们还要进行一下图片预处理(二值化,锐化),最后将标签保存下来,部分代码如下:
length = n*270driver.execute_script('window.scrollTo(0, {}*270)'.format(n))# 保存当前窗口为图片driver.save_screenshot('b.png')# time.sleep(2)# 定位div地址location = div.location# print(location)# 得到div尺寸size = div.size# print(size)left = location['x']top = location['y'] - lengthright = location['x'] + size['width']bottom = location['y'] + size['height'] - length
4. 利用tesseract对保存的图片进行识别,得到票房数据
原始票房数据图片:
预处理后的票房数据图片:
对处理后的图片进行利用tesseract进行识别处理,部分代码如下:
# 调用tesseract进行文字数字识别,目前对于浅颜色的字体识别率较低,若想提高识别率需要对图片做预处理os.system('echo off')#关闭命令行窗口运行命令的显示os.system('tesseract' + ' ' + filename + ' ' + output + ' ' + '-l num+chi_tra') #默认已配置好系统变量time.sleep(2)f = open(output + ".txt", encoding='utf-8')try: t = f.readlines()[0]
目前在没有去训练自己的数据识别包,直接利用tesseract的现有数据包,通过多次试验对实时票房的识别率已达到97%以上,由于总票房字体颜色太浅,且字体较小目前识别率还比较低在60%左右。得到的数据具体如下图所示:
本次初次尝试利用图像识别解决爬虫与到的问题,感觉确实比较好玩,目前获取标签图片的流程已经打通,但图像预处理和图像识别部分还有大量工作要做,目前是采用第三方软件Tesseract-OCR进行的识别,后面一方面会继续研究Tesseract-OCR,另一方面也会尝试利用深度学习的方法提高图片文字的识别精度。