Scrapy实战
Scrapy介绍
-
Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛。
-
框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便。
-
Scrapy 使用了 Twisted['twɪstɪd]
(其主要对手是Tornado)异步网络框架来处理网络通讯,可以加快我们的下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。
### Scrapy架构图
其中绿色的箭头表示数据流动的方向

Scrapy Engine(引擎)
: 负责Spider
、ItemPipeline
、Downloader
、Scheduler
中间的通讯,信号、数据传递等。
Scheduler(调度器)
: 它负责接受引擎
发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎
需要时,交还给引擎
。
Downloader(下载器)
:负责下载Scrapy Engine(引擎)
发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎)
,由引擎
交给Spider
来处理,
Spider(爬虫)
:它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎
,再次进入Scheduler(调度器)
,
Item Pipeline(管道)
:它负责处理Spider
中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方.
Downloader Middlewares(下载中间件)
:你可以当作是一个可以自定义扩展下载功能的组件。
Spider Middlewares(Spider中间件)
:你可以理解为是一个可以自定扩展和操作引擎
和Spider
中间通信
的功能组件(比如进入Spider
的Responses;和从Spider
出去的Requests)
Scrapy的运作流程
代码写好,程序开始运行...
引擎
:Hi!Spider
, 你要处理哪一个网站?
Spider
:老大要我处理xxxx.com。
引擎
:你把第一个需要处理的URL给我吧。
Spider
:给你,第一个URL是xxxxxxx.com。
引擎
:Hi!调度器
,我这有request请求你帮我排序入队一下。
调度器
:好的,正在处理你等一下。
引擎
:Hi!调度器
,把你处理好的request请求给我。
调度器
:给你,这是我处理好的request
引擎
:Hi!下载器,你按照老大的下载中间件
的设置帮我下载一下这个request请求
下载器
:好的!给你,这是下载好的东西。(如果失败:sorry,这个request下载失败了。然后引擎
告诉调度器
,这个request下载失败了,你记录一下,我们待会儿再下载)
引擎
:Hi!Spider
,这是下载好的东西,并且已经按照老大的下载中间件
处理过了,你自己处理一下(注意!这儿responses默认是交给def parse()
这个函数处理的)
Spider
:(处理完毕数据之后对于需要跟进的URL),Hi!引擎
,我这里有两个结果,这个是我需要跟进的URL,还有这个是我获取到的Item数据。
引擎
:Hi !管道
我这儿有个item你帮我处理一下!调度器
!这是需要跟进URL你帮我处理下。然后从第四步开始循环,直到获取完老大需要全部信息。
管道``调度器
:好的,现在就做!
注意:只有当调度器没有request需要处理时,整个程序才会停止。(对于下载失败的URL,Scrapy也会重新下载。)
制作 Scrapy 爬虫4个步骤
- 新建项目 (scrapy startproject xxx):新建一个新的爬虫项目
- 明确目标 (编写items.py):明确你想要抓取的目标
- 制作爬虫 (spiders/xxspider.py):制作爬虫开始爬取网页
- 存储内容 (pipelines.py):设计管道存储爬取内容
配置安装
Scrapy框架官方网址:http://doc.scrapy.org/en/latest
Scrapy中文维护站点:http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html
注意:为了避免scrapy安装包和其它包相冲突,建议使用virtualenv


在virtualenv环境中,我们只需执行如下命令即可安装成功
| pip install Scrapy
# 额外的依赖
pip install pypiwin32
|
安装成功之后,在命令行中输入:scrapy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | Scrapy 1.6.0 - no active project
Usage:
scrapy <command> [options] [args]
Available commands:
bench Run quick benchmark test
fetch Fetch a URL using the Scrapy downloader
genspider Generate new spider using pre-defined templates
runspider Run a self-contained spider (without creating a project)
settings Get settings values
shell Interactive scraping console
startproject Create new project
version Print Scrapy version
view Open URL in browser, as seen by Scrapy
[ more ] More commands available when run from project directory
Use "scrapy <command> -h" to see more info about a command
|
快速入门
在这里,我们以爬取手机端的斗鱼为例,要访问的地址为:
https://m.douyu.com/list/room?type=yz
1.创建项目
执行如下命令:
| scrapy startproject douyu_yz
# 输出的内容如下:
You can start your first spider with:
cd douyu_yz
scrapy genspider example example.com
|
执行完成之后会产生这样的目录:
| │ scrapy.cfg
│
└─douyu_yz
│ items.py
│ middlewares.py
│ pipelines.py
│ settings.py
│ __init__.py
│
├─spiders
|
下面来简单介绍一下各个主要文件的作用:
-
scrapy.cfg :项目的配置文件
-
douyu_yz/ :项目的Python模块,将会从这里引用代码
-
douyu_yz/items.py :项目的目标文件
-
douyu_yz/pipelines.py :项目的管道文件
-
douyu_yz/settings.py :项目的设置文件
-
douyu_yz/spiders/ :存储爬虫代码目录
2.明确目标(Spider)
我们的目标就是要去爬取每一个主播的信息
- 主播头像连接地址
- 主播的名字
- 直播室的标题
- 热点数量
- 打开douyu_yz目录下的items.py
- Item 定义结构化数据字段,用来保存爬取到的数据,有点像Python中的dict,但是提供了一些额外的保护减少错误。
- 可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field的类属性来定义一个Item(可以理解成类似于ORM的映射关系)。
- 接下来,创建一个ItcastItem 类,和构建item模型(model)。
| class DouyuYzItem(scrapy.Item):
# 1. 主播头像连接地址
icon = scrapy.Field()
# 2. 主播的名字
name = scrapy.Field()
# 3. 直播室的标题
title = scrapy.Field()
# 4. 热点数量
hot = scrapy.Field()
|
3.制作爬虫
| 执行: scrapy genspider YanZhiSpider douyu.com
输出: Created spider 'YanZhiSpider' using template 'basic' in module:
douyu_yz.spiders.YanZhiSpider
|
然后在spiders目录中就会自动创建一个爬虫类
| class YanzhispiderSpider(scrapy.Spider):
name = 'YanZhiSpider'
allowed_domains = ['douyu.com']
start_urls = ['http://m.douyu.com/']
def parse(self, response):
pass
|
当然,这个代码你也可以直接纯手工打造
建立一个Spider, 你必须用scrapy.Spider类创建一个子类,并确定了三个强制的属性 和 一个方法。
name = ""
:这个爬虫的识别名称,必须是唯一的,在不同的爬虫必须定义不同的名字。
allowed_domains = []
是搜索的域名范围,也就是爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的URL会被忽略。
start_urls = ()
:爬取的URL元祖/列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成。
parse(self, response)
:解析的方法,每个初始URL完成下载后将被调用,调用的时候传入从每一个URL传回的Response对象来作为唯一参数,主要作用如下:
- 负责解析返回的网页数据(response.body),提取结构化数据(生成item)
- 生成需要下一页的URL请求。
爬取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class YanzhispiderSpider(scrapy.Spider):
name = 'YanZhiSpider'
allowed_domains = ['douyu.com']
start_urls = ['https://m.douyu.com/api/room/list?page=1&type=yz']
def parse(self, response):
jsonData = json.loads(response.text)
users = jsonData["data"]["list"]
items = []
for user in users:
item = DouyuYzItem()
item["name"] = user["nickname"]
item["icon"] = user["roomSrc"]
item["title"] = user["roomName"]
item["hot"] = user["hn"]
items.append(item)
print("items:",items)
return items;
|
保存数据
scrapy保存信息的最简单的方法主要有四种,-o 输出指定格式的文件,,命令如下:
| # json格式,默认为Unicode编码
scrapy crawl YanZhiSpider -o yanzhi.json
# json lines格式,默认为Unicode编码
scrapy crawl YanZhiSpider -o yanzhi.jsonl
# csv 逗号表达式,可用Excel打开
scrapy crawl YanZhiSpider -o teachers.csv
# xml格式
scrapy crawl YanZhiSpider -o teachers.xml
|
Item Pipeline
当Item在Spider中被收集之后,它将会被传递到Item Pipeline,这些Item Pipeline组件按定义的顺序处理Item。
每个Item Pipeline都是实现了简单方法的Python类,比如决定此Item是丢弃而存储。以下是item pipeline的一些典型应用:
- 验证爬取的数据(检查item包含某些字段,比如说name字段)
- 查重(并丢弃)
- 将爬取结果保存到文件或者数据库中
编写Item pipeline
编写item pipeline很简单,item pipiline组件是一个独立的Python类,其中process_item()方法必须实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | import something
class SomethingPipeline(object):
def __init__(self):
# 可选实现,做参数初始化等
# doing something
def process_item(self, item, spider):
# item (Item 对象) – 被爬取的item
# spider (Spider 对象) – 爬取该item的spider
# 这个方法必须实现,每个item pipeline组件都需要调用该方法,
# 这个方法必须返回一个 Item 对象,被丢弃的item将不会被之后的pipeline组件所处理。
return item
def open_spider(self, spider):
# spider (Spider 对象) – 被开启的spider
# 可选实现,当spider被开启时,这个方法被调用。
def close_spider(self, spider):
# spider (Spider 对象) – 被关闭的spider
# 可选实现,当spider被关闭时,这个方法被调用
|
修改之前的案例
item写入JSON文件
以下pipeline将所有(从所有'spider'中)爬取到的item,存储到一个独立地items.json 文件,每行包含一个序列化为'JSON'格式的'item'。
打开 pipelines.py 文件,写入下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | import json
class DouyuYzPipeline(object):
def open_spider(self,spider):
print("启动的时候执行!")
self.file = open("yanzi.txt","wb")
def process_item(self, item, spider):
# 将item对象转成普通的字典,然后转成json,ensure_ascii是使用utf-8编码输出内容
jsonStr = json.dumps(dict(item),ensure_ascii=False)+"\n"
self.file.write(jsonStr.encode())
return item # 仅仅表示这个item我已经存好啦!
def close_spider(self,spider):
print("结束的时候执行!")
self.file.close()
|
启用Pipeline组件
为了启用Item Pipeline组件,必须将它的类添加到 settings.py文件ITEM_PIPELINES 配置,就像下面这个例子:
| # Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'douyu_yz.pipelines.DouyuYzPipeline': 300,
}
|
分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内(0-1000随意设置,数值越低,组件的优先级越高)
重新启动爬虫
注意将spider中的return改为yield返回内容,命令行下重新执行
| scrapy crawl YanZhiSpider
|
查看当前目录是否日志和生成的yanzi.txt文件
保存图片的Pipeline
在scrapy中下载图片是一件非常容易的事情,它已经帮我们封装好了相应的逻辑
我们只需要写一个Pipeline继承ImagesPipeline即可!
注意: 图片下载依赖一个python图片处理库,我们需要先安装如下依赖
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | import scrapy
from scrapy.pipelines.images import ImagesPipeline
from scrapy.utils.project import get_project_settings
class YzImagePipeline(ImagesPipeline):
IMAGES_STORE = get_project_settings().get("IMAGES_STORE")
def get_media_requests(self, item, info):
image_url = item["icon"]
# 发送请求去下载
yield scrapy.Request(image_url)
def item_completed(self, results, item, info):
# 固定写法,获取图片路径,同时判断这个路径是否正确,如果正确,就放到 image_path里,ImagesPipeline源码剖析可见
image_path = [x["path"] for ok, x in results if ok]
# 将图片下载的图路径保存起来
item["image_path"] = self.IMAGES_STORE + "/" + image_path[0]
# 表示这个item我已经处理完啦! 交由下一个管道继续处理
return item
|
Spider
Spider类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是您定义爬取的动作及分析某个网页(或者是有些网页)的地方。
class scrapy.Spider
是最基本的类,所有编写的爬虫必须继承这个类。
主要用到的函数及调用顺序为:
__init__()
: 初始化爬虫名字和start_urls列表
start_requests() 调用make_requests_from url()
:生成Requests对象交给Scrapy下载并返回response
parse()
: 解析response,并返回Item或Requests(需指定回调函数)。Item传给Item pipline持久化 , 而Requests交由Scrapy下载,并由指定的回调函数处理(默认parse()),一直进行循环,直到处理完所有的数据为止。
主要属性和方法
定义spider名字的字符串。
例如,如果spider爬取 mywebsite.com ,该spider通常会被命名为 mywebsite
包含了spider允许爬取的域名(domain)的列表,可选。
初始URL元祖/列表。当没有制定特定的URL时,spider将从该列表中开始进行爬取。
该方法必须返回一个可迭代对象(iterable)。该对象包含了spider用于爬取(默认实现是使用 start_urls 的url)的第一个Request。
当spider启动爬取并且未指定start_urls时,该方法被调用。
当请求url返回网页没有指定回调函数时,默认的Request对象回调函数。用来处理网页返回的response,以及生成Item或者Request对象。
- log(self, message[, level, component])
使用 scrapy.log.msg() 方法记录(log)message。 更多数据请参见 logging
parse()方法的工作机制:
- 因为使用的yield,而不是return。parse函数将会被当做一个生成器使用。scrapy会逐一获取parse方法中生成的结果,并判断该结果是一个什么样的类型;
- 如果是request则加入爬取队列,如果是item类型则使用pipeline处理,其他类型则返回错误信息。
- scrapy取到第一部分的request不会立马就去发送这个request,只是把这个request放到队列里,然后接着从生成器里获取;
- 取尽第一部分的request,然后再获取第二部分的item,取到item了,就会放到对应的pipeline里处理;
- parse()方法作为回调函数(callback)赋值给了Request,指定parse()方法来处理这些请求 scrapy.Request(url, callback=self.parse)
- Request对象经过调度,执行生成 scrapy.http.response()的响应对象,并送回给parse()方法,直到调度器中没有Request(递归的思路)
- 取尽之后,parse()工作结束,引擎再根据队列和pipelines中的内容去执行相应的操作;
- 程序在取得各个页面的items前,会先处理完之前所有的request队列里的请求,然后再提取items。
- 这一切的一切,Scrapy引擎和调度器将负责到底。
Request
常用参数
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 | url: 就是需要请求,并进行下一步处理的url
callback: 指定该请求返回的Response,由那个函数来处理。
method: 请求一般不需要指定,默认GET方法,可设置为"GET", "POST", "PUT"等,且保证字符串大写
headers: 请求时,包含的头文件。一般不需要。内容一般如下:
# 自己写过爬虫的肯定知道
Host: media.readthedocs.org
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/css,*/*;q=0.1
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://scrapy-chs.readthedocs.org/zh_CN/0.24/
Cookie: _ga=GA1.2.1612165614.1415584110;
Connection: keep-alive
If-Modified-Since: Mon, 25 Aug 2014 21:59:35 GMT
Cache-Control: max-age=0
meta: 在不同的请求之间传递数据使用的。字典dict型
request_with_cookies = Request(
url="http://www.example.com",
cookies={'currency': 'USD', 'country': 'UY'},
meta={'dont_merge_cookies': True}
)
encoding: 使用默认的 'utf-8' 就行。
dont_filter: 表明该请求不由调度器过滤。这是当你想使用多次执行相同的请求,忽略重复的过滤器。默认为False。
errback: 指定错误处理函数
|
发送POST请求
- 可以使用
yield scrapy.FormRequest(url, formdata, callback)
方法发送POST请求。
- 如果希望程序执行一开始就发送POST请求,可以重写Spider类的
start_requests(self)
方法,并且不再调用start_urls里的url。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | class mySpider(scrapy.Spider):
# start_urls = ["http://www.example.com/"]
def start_requests(self):
url = 'http://www.renren.com/PLogin.do'
# FormRequest 是Scrapy发送POST请求的方法
yield scrapy.FormRequest(
url = url,
formdata = {"email" : "mr_mao_hacker@163.com", "password" : "axxxxxxxe"},
callback = self.parse_page
)
def parse_page(self, response):
# do something
|
电影天堂爬取案例
创建项目
| scrapy startproject day29
|
创建爬虫
| scrapy genspider MovieSpider ygdy8.net
|
编写爬虫
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 | import scrapy
from day29.items import MovieItem
class MoiveSpider(scrapy.Spider):
name = 'MoiveSpider'
allowed_domains = ['dytt8.net','ygdy8.net']
start_urls = ['http://www.ygdy8.net/html/gndy/dyzz/list_23_1.html']
offset=1
def parse(self, response):
urls = response.css(".ulink::attr(href)").extract()
print("获取结果:",urls)
for url in urls:
url = "http://www.ygdy8.net"+url
yield scrapy.Request(url,callback=self.parse_item)
# 爬取后面的内容
if self.offset < 10:
self.offset+=1
newpageurl = "http://www.ygdy8.net/html/gndy/dyzz/list_23_{}.html".format(self.offset)
yield scrapy.Request(newpageurl,callback=self.parse)
def parse_item(self,response):
name = response.css("#header > div > div.bd2 > div.bd3 > div.bd3r > div.co_area2 > div.title_all > h1 > font::text").extract()[0]
icon = response.css("#Zoom img::attr('src')").extract()[0]
url = response.css("#Zoom td > a::attr('href')").extract()[0]
item = MovieItem()
item['name'] = name
item['icon'] = icon
item['url'] = url
return item
|
管道处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | from sqlalchemy import Column, String,create_engine
from sqlalchemy.dialects.mysql import INTEGER
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class Movie(Base):
__tablename__ = 'movie'
id = Column(INTEGER(11), primary_key=True)
name = Column(String(100))
icon = Column(String(100))
url = Column(String(100))
engine = create_engine("mysql://root:123456@localhost:3306/test?charset=utf8",echo=True)
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
|
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 | from day29.db.models import Session,Movie
from day29.items import MovieItem
class Day29Pipeline(object):
def open_spider(self,spider):
print("打开爬虫:",spider)
self.session = Session()
def process_item(self, item, spider):
print("processitem:",spider)
if isinstance(item,MovieItem):
movie = Movie()
movie.name=item['name']
movie.icon=item['icon']
movie.url=item['url']
# 保存之前先查询数据库是否已经有重名的
result = self.session.query(Movie).filter(Movie.name==item['name']).first()
if not result:
self.session.add(movie)
else:
print("$$$$$$$$$$$$$$$$$$找到数据",result)
print("管道中处理完成:",item)
return item
def close_spider(self,spider):
self.session.commit()
self.session.close()
|
执行爬虫
附录
常见的浏览器信息
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 | # Windows 10 IE 11.0
USER_AGENT="Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"}
# Chrome 65.9
headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"}
# iOS 8.0 Safari
USER_AGENT = "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A365 Safari/600.1.4"
# Android 4.4.4 Chrome
USER_AGENT = "Mozilla/5.0 (Linux; Android 4.4.4; HTC D820u Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.89 Mobile Safari/537.36"
USER_AGENT_LIST = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10",
"Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8",
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5",
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.17) Gecko/20110123 (like Firefox/3.x) SeaMonkey/2.0.12",
"Mozilla/5.0 (Windows NT 5.2; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 SeaMonkey/2.7.1",
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.8 (KHTML, like Gecko) Chrome/4.0.302.2 Safari/532.8",
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.464.0 Safari/534.3",
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.54 Safari/535.2",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.36 Safari/535.7",
"Mozilla/5.0 (Macintosh; U; Mac OS X Mach-O; en-US; rv:2.0a) Gecko/20040614 Firefox/3.0.0 ",
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3",
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5",
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.14) Gecko/20110218 AlexaToolbar/alxf-2.0 Firefox/3.6.14",
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
]
|