之前在上bilibili的时候就想通过python的采集办法来获取视频封面,但是在人工手动解读代码的时候发现了一些问题:BiliBili的主页由JS动态生成,当http去访问时还会重定向到https(主页),所以直接用BeautifulSoup采集的页面所得到的BeautifulSoup对象总是一个空值,并不能够完成采集。
而书本所给的思路则是面对小批量的数据所产生的结果:
python爬虫的最佳实践(五)--selenium+PhantomJS的简单使用
而上面的这个方法试用性强但是模拟浏览器运行js得到结果这个过程应该很累吧,虽然一个无头浏览器PhantomJs配上自动测试工具Selenium算是最小预算了,但是如果面对大批量的评论、用户或者连接的话怕是效率不存在(因为我是老年机也不会干这种大批量的测试)。本来数据采集就是针对某一特性而进行分类管理和下载的方式,本着效率至上的原则一步一步去探究了(没学过js完全慌了)
哇,BILIBILI很调皮啊,直接在主体的第一行就写上了封面的位置,只不过象征性的写了一句display:none,这明显是开的后门。如果我从这个位置拿的话肯定是GET 200的状态拿到了。封面图片PNG/JPG。但是这句是JS渲染上去的属性,也就是说还是得用selenium(所以这是个被废弃的方案,待会做)这样一来则说明想从页面拿是行不通了。
在上面尝试无果之后我换了种思路:之前的API
其实在看到这些json格式的数据时候我就在想这些会是什么东西,挨个查看了之后发现了这些东西里包含了播放数目,已经加载的一些弹幕、评论以及相关视频的一些信息,还有bilibili自己的广告和站题什么的还有用户的头像。但是搜遍了整条街的json也没看见封面,我返回上一级试了试结果又是一堆没我想要的东西的json,我觉得是我打开方式不对,想看看bilibili的api文档结果还被墙了。
然后三折腾四折腾找到了我想要的这个:http://www.bilibili.com/index/ding.json
但是这是一个不定项选择题。并不是指哪取哪的,这样就很尴尬了。经过一番折腾之后我回过头发现,原来selenium也是很好用的的嘛(╮(╯▽╰)╭)于是进入正题:
强大的selenium+PhantomJS
只能说是之前的我太年轻了。因为很多网站毕竟复杂了,可能不同需求就有着不同办法,但是终归能拿到手HTML代码就好操作了,所以有了上面的经历后(特别绝望)顺便查阅了一些相关的文档:当网页使用AJAX动态加载(PJAX也行)的时候我就很麻烦了。页面上有一些简单的文字, 是手工敲在 HTML 代码里的,打开页面两秒钟之后,页面就会被替换成一个Ajax 生成的内容。 如果用传统的方法采集这个页面,只能获取加载前的页面,而真正需要的信息( Ajax 执行之后的页面)却抓不到。然后就凉了。
所以依旧是以上的问题。我们用老套但是非常强大的办法来解决吧:
日常pycharm安装之后去官网下载这个无头浏览器(官网下载好慢啊)办妥这一切之后就很简单了
from bs4 import BeautifulSoup from selenium import webdriver import time import re driver = webdriver.PhantomJS(executable_path='C:/Users/Administrator/Desktop/phantomjs-2.1.1-windows/bin/phantomjs') driver.get("https://www.bilibili.com/video/av12092863/index.html") time.sleep(4) pageSource = driver.page_source bsObj = BeautifulSoup(pageSource,"html.parser") images = bsObj.findAll("img", {"src": re.compile("//cdn.kelovp.tech/.*\.jpg")}) print "http:"+images[0].get("src") driver.close()
由于我自己失了志直接把压缩包解压在了桌面,所以webdriver的驱动路径也是在桌面,我让浏览器等4秒再去看看。然后把网页源码返回给我,生成beautifulsoup对象,然后就是老套路了。。
封面图片直接搞定,这个组合意外的好用。
处理重定向
这是之前采集就遇到的一个非常让我头疼的事情,这里区分一下:由于是客户端重定向是在服务器将页面内容发送到浏览器之前,由浏览器执行 JavaScript 完成的页面跳转,而不是服务器完成的跳转。当使用浏览器访问页面的时候,有时很难区分这两种重定向。由于客户端重定向执行很快,加载页面时你甚至感觉不到任何延迟,所以会让你觉得这个重定向就是一个服务器端重定向。但是, 在进行网络数据采集的时候,这两种重定向的差异是非常明显的。根据具体情况,服务器端重定向一般都可以轻松地通过 Python 的 urllib 库解决,不需要使用 Selenium。客户端重定向却不能这样处理, 除非有工具可以执行JavaScript。说白了之前我写的程序的等待就是为了等这些个JS执行完毕再去采集,很难说这些东西什么时候执行完全,所以可以通过定时检查来看看页面是否发生了跳转。
这里我没找到什么好的例子来执行,那书上的程序来测试吧:
from selenium import webdriver import time from selenium.webdriver.remote.webelement import WebElement from selenium.common.exceptions import StaleElementReferenceException def waitForLoad(driver): elem = driver.find_element_by_tag_name("html") count = 0 while True: count += 1 if count > 20: print("Timing out after 10 seconds and returning") return time.sleep(.5) try: elem == driver.find_element_by_tag_name("html") except StaleElementReferenceException: return driver = webdriver.PhantomJS(executable_path='D:/phantomjs/bin/phantomjs') driver.get("http://pythonscraping.com/pages/javascript/redirectDemo1.html") waitForLoad(driver) code = driver.page_source driver.close() print(code)
这个程序每半分钟检查一次网页,看看 html 标签还在不在,时限为 10 秒钟,不过检查的时间间隔和时限都可以根据实际情况随意调整。