Contents
  1. 1. Preliminaries
    1. 1.1. 什么是动态网页
    2. 1.2. Selenium
  2. 2. 编写 Spider
    1. 2.1. 获得检索商品页面
    2. 2.2. 解析网页
  3. 3. 结论

  前面一文Scrapy爬虫抓取网站数据已经介绍了如何实现一个爬虫,文中介绍静态bbs网页的抓取。但是,互联网大部分的web页面都是动态的,经常逛的网站例如京东、淘宝等,商品列表都是js,并有Ajax渲染,这样就获取不到网页内容(获取到后台数据后再组合成html展示出来的)。单纯获取页面而没有执行到js的话是无法看到商品数据列表信息的。
  那么,我们可以怎么获取这些数据呢?本文以典型的比价网站为例,如:搜狗购物一淘网,抓取给定商品的价格及其来源网站,如下图。详见完整的爬虫代码
  Search list
  抓取内容

Preliminaries

  抓取前先了解一些概念和工具。

什么是动态网页

  动态网页是指跟静态网页相对的一种网页编程技术。静态网页,随着html代码的生成,页面的内容和显示效果就基本上不会发生变化了——除非你修改页面代码。而动态网页则不然,页面代码虽然没有变,但是显示的内容却是可以随着时间、环境或者数据库操作的结果而发生改变的。与静态网页相对应的,能与后台数据库进行交互,数据传递。也就是说,网页 URL的后缀不是.htm、.html、.shtml、.xml等静态网页的常见形动态网页制作格式,而是以.aspx、.asp、.jsp、.php、.perl、.cgi等形式为后缀,并且在动态网页网址中有一个标志性的符号——“?”。可以通过以下方式简单验证某网页是否为动态网页。

  在页面上右键查看源代码,和右键审查元素所看到的html代码是不一样的,如果后者中能看到商品数据信息,而前者没有的话,就说明这个页面是动态生成的。

Selenium

  Selenium是Thoughtworks公司的一个集成测试的强大工具。Selenium 是 ThoughtWorks 专门为 Web 应用程序编写的一个验收测试工具。与其他测试工具相比,使用 Selenium 的最大好处是: Selenium 测试直接在浏览器中运行,就像真实用户所做的一样。在浏览器加载js后,便可以通过xpath来解析网页了。可以先用 pip install 或 easy_install 安装 Selenium package。

1
pip install -U selenium

编写 Spider

  这里就不详细说明 itempipelinesetting文件的编写了。如果对这些模块不熟的话可以先看看 Our first spider

获得检索商品页面

  搜索某一款产品,如 Iphone6,我们就可以得到该产品检索结果的起始页面的 start_urls。但通常情况下,我们可能要得到很多商品相应的信息,那该怎么处理呢?容易想到的是让浏览器模拟我们手动输入,自动响应检索事件,从而得到目标页面的 start_urls。另一种方法是,我们将目标产品存到一个配置文件中,直接将http://gouwu.sogou.com/shop?query=+ ItemList 作为 start_urls。这里采用后一种方法,简单粗暴有效。

1
2
#import lstData
start_urls = [ ('http://gouwu.sogou.com/shop?query=' + searchWord ) for searchWord in lstData().lst]

解析网页

  首先启用 selenium,这里用本地浏览器 Firefox:

1
2
3
4
5
6
7
def __init__(self):
CrawlSpider.__init__(self)
# use any browser you wish
self.browser = webdriver.Firefox()
def __del__(self):
self.browser.close()

  得到所有产品的 start_urls 后,我们便可以通过 Xpath 提取想要的数据了。这里抓取的内容有标题、价格和来源网站。

1
2
3
4
5
_x_query = {
'title': '//p[@class="title"]/a/@title',
'price' : '//span[@class="shopprice font17"]/text()',
'name': '//span[@class="floatR hui61 mt1"]/text()',##source
}

 然后提取页面(下一页),定义提取和过滤 url

1
2
3
4
link_extractor = {
'page_down': SgmlLinkExtractor(allow = '/shop\?query=.+',),#restrict_xpaths = '//a[@class = "pagination-next"]'
'page': SgmlLinkExtractor(allow = '/detail/\d+\.html.+'),
}

  这样我们的 Spider 就差不多定义好了。完整 Spider 程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#-*- coding: utf-8 -*-
'''
gouwu.sogou.com Spider, Created on Dec, 2014
#version: 1.0
#author: chenqx @http://chenqx.github.com
See more: http://doc.scrapy.org/en/latest/index.html
'''
import time
from scrapy.selector import Selector
from scrapy.http import Request
from scrapy.contrib.spiders import CrawlSpider
from scrapy.contrib.loader import ItemLoader
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from etao.items import EtaoItem
from etao.lstData import lstData
from selenium import webdriver
class etaoSpider(CrawlSpider):
# name of spiders
name = 'Spider'
allow_domain = ['gouwu.sogou.com']
start_urls = [ ('http://gouwu.sogou.com/shop?query=' + searchWord ) for searchWord in lstData().lst]
link_extractor = {
'page': SgmlLinkExtractor(allow = '/detail/\d+\.html.+'),
'page_down': SgmlLinkExtractor(allow = '/shop\?query=.+',),#restrict_xpaths = '//a[@class = "pagination-next"]'
}
_x_query = {
'title': '//p[@class="title"]/a/@title',
'name': '//span[@class="floatR hui61 mt1"]/text()',#//li[2]/a/div[@class="ruyitao-market-name ruyitao-market-name-hightlight"]/text()
'price' : '//span[@class="shopprice font17"]/text()', # 'price' : '//span[@class = "price"]/text()',
}
def __init__(self):
CrawlSpider.__init__(self)
# use any browser you wish
self.browser = webdriver.Firefox()
def __del__(self):
self.browser.close()
def parse(self, response):
#crawl all display page
for link in self.link_extractor['page_down'].extract_links(response):
yield Request(url = link.url, callback=self.parse)
#start browser
self.browser.get(response.url)
#loading time interval
time.sleep(5)
# get the data and write it to scrapy items
etaoItem_loader = ItemLoader(item=EtaoItem(), response = response)
url = str(response.url)
etaoItem_loader.add_value('url', url)
etaoItem_loader.add_xpath('title', self._x_query['title'])
etaoItem_loader.add_xpath('name', self._x_query['name'])
etaoItem_loader.add_xpath('price', self._x_query['price'])
yield etaoItem_loader.load_item()

结论

  本文介绍了利用 selenium 实现动态网站数据抓取的一种方法。但需要注意的是 selenium 需要运行本地浏览器,比较耗时,不太适合大规模网页抓取。因此可以尝试其它的 Javascript 加载工具,如 webkit、spynner,也可以调用无界面依赖的浏览器引擎 Casperjs、Phantomjs等。   

Contents
  1. 1. Preliminaries
    1. 1.1. 什么是动态网页
    2. 1.2. Selenium
  2. 2. 编写 Spider
    1. 2.1. 获得检索商品页面
    2. 2.2. 解析网页
  3. 3. 结论