本帖最后由 xandy 于 2016-6-23 09:55 编辑

1,引言

本文讲解怎样用Python驱动Firefox浏览器写一个简易的网页数据采集器。GooSeeker整个Python开源爬虫项目将与Scrapy(基于twisted的异步网络框架)集成,所以本例将使用Scrapy采集淘宝这种含有大量ajax代码的网页数据,但是要注意本例一个严重缺陷:用Selenium加载网页的过程发生在Spider中,破坏了Scrapy的架构原则。所以,本例只是为了测试Firefox驱动和ajax网页数据采集这两个技术点,用于正式运行环境中必须予以修改,后续的文章将专门讲解修正后的实现。
请注意,本例用到的xslt文件是通过MS谋数台保存提取器后,通过API接口获得,一方面让python代码变得简洁,另一方面,节省调试采集规则的时间。详细操作请查看GooSeeker API说明(1)--下载内容提取器

2,具体实现

2.1,环境准备

需要执行以下步骤,准备Python开发和运行环境:
  • 安装Python--官网下载安装并部署好环境变量 (本文使用Python版本为3.5.1)
  • 安装lxml-- 官网库下载对应版本的.whl文件,然后命令行界面执行 "pip install .whl文件路径"
  • 安装Scrapy--命令行界面执行 "pip install Scrapy",详细请参考Scrapy的第一次运行测试
  • 安装selenium--命令行界面执行 "pip install selenium"
  • 安装Firefox--官网下载安装
上述步骤展示了两种安装:1,安装下载到本地的wheel包;2,用Python安装管理器执行远程下载和安装。

2.2,开发和测试过程


以下代码默认都是在命令行界面执行

1),创建scrapy爬虫项目simpleSpider

  1. E:\python-3.5.1>scrapy startproject simpleSpider
复制代码

2
),修改settings.py配置

有些网站会在根目录下放置一个名字为robots.txt的文件,里面声明了此网站希望爬虫遵守的规范,Scrapy默认遵守这个文件制定的规范,即ROBOTSTXT_OBEY默认值为True。在这里需要修改ROBOTSTXT_OBEY的值,找到E:\python-3.5.1\simpleSpider\simpleSpider下文件settings.py,更改ROBOTSTXT_OBEY的值为False

3)
,导入API模块


在项目目录E:\python-3.5.1\simpleSpider下创建文件gooseeker.py(也可以在GooSeeker开源Python网络爬虫GitHub源 的core文件夹中直接下载),代码如下:
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # 模块名: gooseeker
  4. # 类名: GsExtractor
  5. # Version: 2.0
  6. # 说明: html内容提取器
  7. # 功能: 使用xslt作为模板,快速提取HTML DOM中的内容。
  8. # released by 集搜客(http://www.gooseeker.com) on May 18, 2016
  9. # github: https://github.com/FullerHua/jisou/core/gooseeker.py

  10. from urllib import request
  11. from urllib.parse import quote
  12. from lxml import etree
  13. import time

  14. class GsExtractor(object):
  15.     def _init_(self):
  16.         self.xslt = ""
  17.     # 从文件读取xslt
  18.     def setXsltFromFile(self , xsltFilePath):
  19.         file = open(xsltFilePath , 'r' , encoding='UTF-8')
  20.         try:
  21.             self.xslt = file.read()
  22.         finally:
  23.             file.close()
  24.     # 从字符串获得xslt
  25.     def setXsltFromMem(self , xsltStr):
  26.         self.xslt = xsltStr
  27.     # 通过GooSeeker API接口获得xslt
  28.     def setXsltFromAPI(self , APIKey , theme, middle=None, bname=None):
  29.         apiurl = "http://www.gooseeker.com/api/getextractor?key="+ APIKey +"&theme="+quote(theme)
  30.         if (middle):
  31.             apiurl = apiurl + "&middle="+quote(middle)
  32.         if (bname):
  33.             apiurl = apiurl + "&bname="+quote(bname)
  34.         apiconn = request.urlopen(apiurl)
  35.         self.xslt = apiconn.read()
  36.     # 返回当前xslt
  37.     def getXslt(self):
  38.         return self.xslt
  39.     # 提取方法,入参是一个HTML DOM对象,返回是提取结果
  40.     def extract(self , html):
  41.         xslt_root = etree.XML(self.xslt)
  42.         transform = etree.XSLT(xslt_root)
  43.         result_tree = transform(html)
  44.         return result_tree
复制代码

4
),创建SimpleSpider爬虫类

在项目目录E:\python-3.5.1\simpleSpider\simpleSpider\spiders下创建文件simplespider.py,代码如下:
  1. # -*- coding: utf-8 -*-
  2. import time
  3. import scrapy
  4. from lxml import etree
  5. from selenium import webdriver
  6. from gooseeker import GsExtractor

  7. class SimpleSpider(scrapy.Spider):
  8.     name = "simplespider"
  9.     allowed_domains = ["taobao.com"]
  10.     start_urls = [
  11.         "https://item.taobao.com/item.htm?spm=a230r.1.14.197.e2vSMY&id=44543058134&ns=1&abbucket=10"
  12.     ]

  13.     def __init__(self):
  14.         # use any browser you wish
  15.         self.browser = webdriver.Firefox()
  16.    
  17.     def getTime(self):
  18.         # 获得当前时间戳
  19.         current_time = str(time.time())
  20.         m = current_time.find('.')
  21.         current_time = current_time[0:m]
  22.         return current_time
  23.       
  24.     def parse(self, response):
  25.         print("start...")
  26.         #start browser
  27.         self.browser.get(response.url)
  28.         #loading time interval
  29.         time.sleep(3)
  30.         #get xslt
  31.         extra = GsExtractor()
  32.         extra.setXsltFromAPI("API KEY" , "淘宝天猫_商品详情30474")
  33.         # get doc
  34.         html = self.browser.execute_script("return document.documentElement.outerHTML");
  35.         doc = etree.HTML(html)
  36.         result = extra.extract(doc)
  37.         # out file
  38.         file_name = 'F:/temp/淘宝天猫_商品详情30474_' + self.getTime() + '.xml'
  39.         open(file_name,"wb").write(result)
  40.         self.browser.close()
  41.         print("end")      
复制代码

5)
,启动爬虫


在E:\python-3.5.1\simpleSpider项目目录下执行命令
  1. E:\python-3.5.1\simpleSpider>scrapy crawl simplespider
复制代码

6
),输出文件

采集到的网页数据结果文件是:淘宝天猫_商品详情30474_1466064544.xml


3,展望

调用Firefox,IE等全特性浏览器显得有点太重量级,很多场合可以考虑轻量级的浏览器内核,比如,casperjs和phantomjs等。同时运行在没有界面的浏览器(headless browser,无头浏览器)模式下,也许可以对网页数据采集性能有所提升。

然后,最重要的一点是要写一个 scrapy 的下载器,专门驱动这些浏览器采集网页数据,也就是把这个功能从Spider中迁移出来,这样才符合Scrapy的整体框架原则,实现事件驱动的工作模式。

4,相关文档

1, GooSeeker API说明(1)--下载内容提取器
2, GooSeeker API例子——用Java下载内容提取器
3, GooSeeker API例子——用JavaScript下载内容提取器

5,集搜客GooSeeker开源代码下载源

1, GooSeeker开源Python网络爬虫GitHub源

6,文档修改历史
1,2016-06-17:V1.0
2, 2016-06-19:V1.1,在第一段明显位置注明本案例的缺陷

举报 使用道具
| 回复

共 9 个关于本帖的回复 最后回复于 2017-8-3 09:29

沙发
shenzhenwan10 金牌会员 发表于 2016-6-17 12:10:11 | 只看该作者
挺好的
举报 使用道具
板凳
shenzhenwan10 金牌会员 发表于 2016-6-17 12:14:35 | 只看该作者
不过有个疑问:
方法 parse(self, response)
被调用的时候,页面已经被访问过一次了,已经有response了。
在parse里再次用webdriver访问,拿到dom。
是不是同一网址访问了2次?
举报 使用道具
地板
ippfon 版主 发表于 2016-6-17 14:16:23 | 只看该作者
shenzhenwan10 发表于 2016-6-17 12:14
不过有个疑问:
方法 parse(self, response)
被调用的时候,页面已经被访问过一次了,已经有response了。

因为Scrapy爬虫的下载器默认只能获取静态网页内容,而示例网站需要加载js后才能获取网页完整内容,所以需要再次调用webdriver访问并加载完js后取网页Dom。
后面会考虑写一个下载handler,结合没有界面的浏览器,让爬虫可以直接得到加载js后的动态页面Dom
举报 使用道具
5#
shenzhenwan10 金牌会员 发表于 2016-6-19 09:53:47 | 只看该作者
在Scrapy的文档中看到有个参数:AJAXCRAWL_ENABLED
缺省是false
楼主可以试试把这个参数打开来爬取ajax页面

举报 使用道具
6#
Fuller 管理员 发表于 2016-6-19 17:03:12 | 只看该作者
shenzhenwan10 发表于 2016-6-19 09:53
在Scrapy的文档中看到有个参数:AJAXCRAWL_ENABLED
缺省是false
楼主可以试试把这个参数打开来爬取ajax页面 ...

研究一下这个参数打开以后,Scrapy使用了什么js引擎?
举报 使用道具
7#
Fuller 管理员 发表于 2016-6-19 18:46:38 | 只看该作者
ippfon 发表于 2016-6-17 14:16
因为Scrapy爬虫的下载器默认只能获取静态网页内容,而示例网站需要加载js后才能获取网页完整内容,所以需 ...

我在文档第一段注明了这个缺陷,让读者知道还要向哪个方向去优化
举报 使用道具
8#
hinson123 新手上路 发表于 2016-8-25 16:22:10 | 只看该作者
通篇看了python的所有文章,文章结构很乱,楼主应该要把这些步骤实现需要的工具、方法和细节一步步展示出来。
举报 使用道具
9#
hinson123 新手上路 发表于 2016-8-25 16:26:29 | 只看该作者
比如我要去安装lxml 库,找了半天,都不知道去哪里找,终于找到了下载链接,进去一看,几十个资源,又不知道下载哪个版本才正确。
可以参考liaoxuefeng这个python的作者的写教程的风格建议楼主学一学,简单可操作,每个帖子在最后都留下了一键下载链接,不用满世界去找。
举报 使用道具
10#
mingdongtianxia 中级会员 发表于 2017-8-3 09:29:30 | 只看该作者
原淘宝页面商品已经下架了,希望赶紧更新修改
举报 使用道具
您需要登录后才可以回帖 登录 | 立即注册

精彩推荐

  • Gephi社会网络分析-马蜂窝游记文本分词并同
  • Gephi社会网络分析-基于马蜂窝游记文本以词
  • 知乎话题文本根据词语间距筛选后生成共词矩
  • 马蜂窝游记文本分词后以词语间距为筛选条件
  • 学习使用apriori算法挖掘关联关系

热门用户

GMT+8, 2024-4-20 02:42