不管是使用beautifulsoup还是scrapy的过程中,都会发现明明网页的源代码里有的数据,但是就是抓取不出来,原因呢就是因为这个数据是由js生成的动态数据。
要抓取这个动态数据一般来说是有2种方法。一种是直接找到js的数据源,那样就会得到一个xml或者是json的数据,后续处理一下就可以了。另外一种就是浏览器直接执行完js,然后再去抓取得到的数据。
比较下2种方法的优缺点。速度上:第一种就是快,相当于直接去获取到了数据。而第2种因为要等整个浏览器执行完js之后再去获取,速度简直是天差地别。便利性:第一种就不够方便了,首先需要花费精力在开发者工具中查找数据源,这本身可能就比较麻烦。大规模爬取的时候数据源没有规律,不能从同一个数据源读取数据,所以大规模爬取的时候就希望一种通用的方法。这时候就是第2种上场的时候了。不需要寻找什么,只要将执行完js之后的数据爬取出来就ok了,通用性强。
我们以scrapy作为框架,来看看京东的数据抓取。
from scrapy_redis.spiders import RedisSpider
from JDspider.items import JDspiderLoader
from splinter import Browser
from scrapy import log
class Myspider(RedisSpider):
'''spider that reads urls from redis queue (myspider:start_urls).'''
name = 'jdspider'
redis_key = 'jdspider_urls'
def __init__(self, *args, **kwargs):
domain = kwargs.pop('domain', '')
self.allowed_domans = filter(None, domain.split(','))
super(Myspider, self).__init__(*args, **kwargs)
def parse(self, response):
el = JDspiderLoader(response=response)
el.add_xpath('title', '//*[@id="name"]/h1/text()')
with Browser() as browser:
url = response.url
browser.visit(url)
price = browser.find_by_id('jd-price')
if price == []:
price = browser.find_by_xpath('//*[@id="price"]/strong')
# self.log(price[0].value, level=log.DEBUG)
el.add_value('price', price[0].value[1:])
with Browser() as browser:
number = response.url.split('/')[-1].split('.')[0]
url = 'http://club.jd.com/review/' + number + '-2-1.html'
browser.visit(url)
shaitu = browser.find_by_xpath('//*[@id="comments-list"]/div[1]/ul/li[5]/a/em')
el.add_value('shaitu', shaitu[0].value[1:-1])
haoping = browser.find_by_xpath('//*[@id="comments-list"]/div[1]/ul/li[2]/a/em')
el.add_value('haoping', haoping[0].value[1:-1])
zhongping = browser.find_by_xpath('//*[@id="comments-list"]/div[1]/ul/li[3]/a/em')
el.add_value('zhongping', zhongping[0].value[1:-1])
chaping = browser.find_by_xpath('//*[@id="comments-list"]/div[1]/ul/li[4]/a/em')
el.add_value('chaping', chaping[0].value[1:-1])
return el.load_item()
我们这里用到的库是splinter。具体的我就不介绍了,它会调用firefox来加载完界面,完成解析。所以如果需要使用这个库的话,需要安装Firefox。
下面看具体的代码。代码其实很简单。
with Browser() as browser:
url = response.url
browser.visit(url)
price = browser.find_by_id('jd-price')
if price == []:
price = browser.find_by_xpath('//*[@id="price"]/strong')
# self.log(price[0].value, level=log.DEBUG)
el.add_value('price', price[0].value[1:])
从splinter里import进来Browser,看名字就知道是浏览器,然后with的上下文管理器里面的内容就是一个简单的抓取价格。既可以用browser.find_by_id()来获取数据,也可以用browser.find_by_xpath()来获取数据。(之所以有2个,是因为京东的价钱有2种格式的,第一种获取不到的话就使用第2种。)用起来非常简单。
splinter的手册:https://splinter.readthedocs.io/en/latest/
以上代码可以从https://github.com/qcl643062/spider/tree/master/JDspider获得,当然了,这只是一部分,其他的还未完成。