1scrapy的使用
1.1scrapy爬取数据流程
scrapy由5个模块构成,分别是engine(引擎),spiders(爬虫),scheduler(调度),downloader(爬取),item pipelines(管道)
Scrapy中的数据流由执⾏行行引擎控制,其过程如下: 1、引擎打开一个网站(open a domain),找到处理理该⽹网站的Spider并向该spider请求第一个要 爬取的URL(s)。 2、引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。 3、引擎向调度器请求下一个要爬取的URL。 4、调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)⽅方向)转 发给下载器(Downloader)。 5、一旦⻚页⾯面下载完毕,下载器⽣生成⼀一个该⻚页⾯面的Response,并将其通过下载中间件(返回 (response)⽅方向)发送给引擎。 6、引擎从下载器中接收到Response并通过Spider中间件(输⼊入⽅方向)发送给Spider处理理。 7、Spider处理理Response并返回爬取到的Item及(跟进的)新的Request给引擎。 8、引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度 器。 9、(从第二步)重复直到调度器中没有更更多地request,引擎关闭该⽹网站。
1.2scrapy安装
linux中直接跳过windows安装过程中的一二步(见下)
windows中需要安装其他插件;过程如下:
1.进入http://www.lfd.uci.edu/~gohlke/pythonlibs/;搜索Twisted
然后在cmd中输入python查看python版本及计算机为多少位(本次选择3.6版本64位);
本次下载Twisted-18.9.0-cp36-cp36m-win_amd64.whl版本
2.在cmd中进入存放虚拟环境的文件夹使用activate激活环境;然后退到存放第一步所下载的文件的文件夹。使用命令pip install Twisted-18.9.0-cp36-cp36m-win_amd64.whl将其安装到虚拟环境;
3.安装pypiwin32 :pip install pypiwin32
4.安装scrapy: pip install scrapy
1.3创建项目及spider
创建项目:命令行中输入scrapy startproject 项目名称
如scrapy startproject sinanews
进入项目后创建spider: 命令行中输入 scrapy genspider 名称 去掉http://的域名
如scrapy genspider sport sports.sina.com.cn
1.4配置项目
打开项目后将settings中的ROBOTSTXT_OBEY = True改为ROBOTSTXT_OBEY = False;
在最后添加编码格式:FEED_EXPORT_ENCODING = 'utf-8'
运行spider文件:scrapy crawl sina -o result.json表示运行spider文件sina并将爬取数据存放到result.json文件中(会自动创建该文件),存放数据的类型有json、csv、xml及pickle
scrapy crawl sina表示运行spider文件sina;但不将结果保存到本地。
spider文件部分参数的作用
allowed_domains: 允许爬取的域名;start_urls: Spider启动时爬取的url列表 ;parse: 负责解析返回的响应,提取数据或进一步处理理
item文件的作用
Item是保存爬取数据的容器
2mysql使用
python中的eval用法
eval(字符串) 能将字符串当成程序运行
mysql中清空数据库的两种方法及区别
truncate table manhua清空数据库,若添加新数据id从1开始
delete from manhau清空数据库,若添加新数据id将从之前的后面接着续
3一个简单的scrapy项目-sinanews(get请求)
spider文件中爬取数据
import scrapy
from sinanews.items import SinanewsItem
class SinaSpider(scrapy.Spider):
name = 'sina'
allowed_domains = ['sports.sina.com.cn']
start_urls = ['http://sports.sina.com.cn/']
def parse(self, response):
reult=response.selector.css('.ty-card-type10-makeup a::text').extract()
for title in reult:
item = SinanewsItem()
item['title'] = title
yield item
items文件中保存数据
import scrapy
class SinanewsItem(scrapy.Item):
title = scrapy.Field()
reult=response.selector.css('.ty-card-type10-makeup a::text').extract()该句表示获取class为该类型下的a标签中的text内容返回一个对象,后面接extract()表示获取其中的内容。
4爬取虎扑网站数据(get请求)
scrapy中处理数据用Selector选择器;其基于lxml构建,支持xpath, css, 正则表达式匹配
import scrapy
from hupu.items import HupuItem
class SproteSpider(scrapy.Spider):
name = 'sprote'
allowed_domains = ['bbs.hupu.com']
start_urls = ['http://bbs.hupu.com/gear/']
# 构造请求
def start_requests(self):
headers = {
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)',
'Accept': 'application/json, text/plain, */*',
'Accept-Encoding': 'gzip, deflate, sdch',
'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4,zh-TW;q=0.2,mt;q=0.2',
'Connection': 'keep-alive',
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
}
for page in range(1, 101):
yield scrapy.Request(url='http://bbs.hupu.com/gear-%d' % page,
headers=headers,
method='GET',
callback=self.parse,
)
# 解析响应
def parse(self, response):
result_list = response.selector.css('.for-list li')
for index in result_list:
item = HupuItem()
title = index.css('.truetit::text').extract_first()
author = index.css('.aulink::text').extract_first()
ti_date = index.css('.author.box').xpath('./a[2]/text()').extract_first()
item['title'] = title
item['author'] = author
item['ti_date'] = ti_date
yield item
items中存取数据
import scrapy
class HupuItem(scrapy.Item):
title = scrapy.Field()
author = scrapy.Field()
ti_date = scrapy.Field()
通过selector获取到对象后,仍然可以继续对其使用xpath,css,等选择器继续选择。
result_list = response.selector.css('.for-list li')获取到result_list一个大的对象
title = index.css('.truetit::text').extract_first()继续对其使用css选择器并获取获取到的第一个数据
ti_date = index.css('.author.box').xpath('./a[2]/text()').extract_first()对其联合使用css选择器和xpath选择器
result = response.css('.author.box').xpath('./a[2]/text()').re('(.?)-.?-')对其联合使用xpath,css及正则选择器
5爬取有妖气网站(post请求)
5.1爬取数据
import json
import re
import scrapy
from u17.items import U17Item
class ComicSpider(scrapy.Spider):
name = 'comic'
allowed_domains = ['www.u17.com']
start_urls = ['http://www.u17.com/']
def start_requests(self):
headers = {
'Referer': 'http://www.u17.com/comic/ajax.php?mod=comic_list&act=comic_list_new_fun&a=get_comic_list',
'User-Agent': "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
'Host': 'www.u17.com',
'Accept': 'application/json, text/javascript, */*;', # headers中requests headers中的accept数据
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4,zh-TW;q=0.2,mt;q=0.2',
'Connection': 'keep-alive',
'X-Requested-With': 'XMLHttpRequest',
}
url = 'http://www.u17.com/comic/ajax.php?mod=comic_list&act=comic_list_new_fun&a=get_comic_list'
data = {'data[group_id]': 'no', 'data[theme_id]': 'no', 'data[is_vip]': 'no', 'data[accredit]': 'no',
'data[color]': 'no', 'data[comic_type]': 'no', 'data[series_status]': '1', 'data[order]': '1',
'data[page_num]': '1', 'data[read_mode]': 'no'
}
for page in range(5):
data['data[page_num]'] = str(page)
print(page)
yield scrapy.FormRequest(url=url,
headers=headers,
method='POST',
formdata=data,
callback=self.parse,
)
def parse(self, response):
result=json.loads(response.text)
result_list = result['comic_list']
pattern = re.compile('<font.*?>(.*?)<.*?', re.S)
for item in result_list:
u17_item = U17Item()
u17_item['comic_id'] = item['comic_id']
u17_item['name'] = item['name']
u17_item['cover'] = item['cover']
u17_item['category'] = re.findall(pattern, item['line2'])[0]
yield u17_item
注意:在循环提交请求的网页数据时;需将Request改为FormRequest;method改为POST
items中保存数据
import scrapy
class U17Item(scrapy.Item):
comic_id = scrapy.Field()
name = scrapy.Field()
cover = scrapy.Field()
category = scrapy.Field()
该文件中保存数据的字段必须与spider文件中给对象添加属性的字段保持一致。
然后在settings文件中修改ROBOTSTXT_OBEY为False;添加编码方式FEED_EXPORT_ENCODING = 'utf-8'。
5.2保存数据
数据保存在管道(pipelines)中进行
5.2.1mysql中保存数据
import pymysql
import pymongo
from scrapy import Request
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline
class U17MysqlPipeline(object):
def __init__(self, host, port, username, password, database):
self.host = host
self.port = port
self.username = username
self.password = password
self.database = database
@classmethod
def from_crawler(cls, crawler):
return cls(
host=crawler.settings.get('MYSQL_HOST'),
port=crawler.settings.get('MYSQL_PORT'),
database=crawler.settings.get('MYSQL_DATABASE'),
username=crawler.settings.get('MYSQL_USERNAME'),
password=crawler.settings.get('MYSQL_PASSWORD'),
)
def open_spider(self, spider):
self.db = pymysql.connect(self.host, self.username,
self.password, self.database, charset='utf8',
port=self.port)
self.cursor = self.db.cursor()
def close_spider(self, spider):
self.db.close()
def process_item(self, item, spider):
sql = 'insert into manhua (comic_id, name, cover, category)
values (%s,%s,%s,%s)'
self.cursor.execute(sql, (item['comic_id'], item['name'],
item['cover'], item['category']))
self.db.commit()
return item
5.2.2mongodb中保存数据
class U17MongoPipeline(object):
def __init__(self, uri, database):
self.uri = uri
self.database = database
@classmethod
def from_crawler(cls, crawler):
return cls(
uri=crawler.settings.get('MONGO_URI'),
database=crawler.settings.get('MONGO_DB'),
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.uri)
self.db = self.client[self.database]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
self.db['manhua'].insert(dict(item))
return item
5.2.3下载图片
class U17ImagePipeline(ImagesPipeline):
# 准备图片文件名
def file_path(self, request, response=None, info=None):
url = request.url
file_name = url.split('/')[-1]
return file_name
# 判断图片是否下载成功,丢弃不成功的
def item_completed(self, results, item, info):
image_paths = [x['path'] for ok, x in results if ok]
if not image_paths:
raise DropItem('Image Downloaded Failed')
return item
# 指明图片下载链接,并包装成request对象
def get_media_requests(self, item, info):
yield Request(item['cover'])
5.2.4保存路径的配置
将上面定义的三个保存数据的类添加到settings中的ITEM_PIPELINES中(68行处)
ITEM_PIPELINES = {
# 前面是管道,后面是优先级,数值越小,优先级越高
'u17.pipelines.U17MysqlPipeline': 300,
'u17.pipelines.U17MongoPipeline': 320,
'u17.pipelines.U17ImagePipeline': 340,
}
'u17.pipelines.U17MysqlPipeline': 300, 前面是管道,后面是优先级,数值越小,优先级越高。将上面定义的保存数据的类添加到此处,运行spider文件,则会自动保存文件到对应的路径;当不需要保存到某个数据库时,在此处注释对应的方法就可以。
然后在最末行添加下面语句
在添加图片路径前,需要先在工程目录下创建images文件夹
# 配置图片目录
IMAGES_STORE = './images'
# mysql 设置
MYSQL_HOST = '127.0.0.1'
MYSQL_PORT = 3306
MYSQL_USERNAME = 'root'
MYSQL_PASSWORD = '123456'
MYSQL_DATABASE = 'u17'
# MONGO 配置
MONGO_URI = 'mongodb://127.0.0.1:27017'
MONGO_DB = 'u17'