Scrapy使用记录

文章目录

  • 一、基本应用
  • 二、知识点
    • 1.理解Downloader Middleware
    • 2.理解Spider Middleware
    • 3.理解Item Pipeline
    • 4.理解yield构建生成器
  • 三、实用技巧
    • 1.定制user-agent
      • 方法1
      • 方法2 (推荐)
    • 2.从XHR类型的页面爬取数据
    • 3.Scrapy对接Splash
      • 3.1 添加配置
      • 3.2 使用

一、基本应用

(PS:图片取自百度搜图,图片上的水印有些糊,看不清来源了.)这个图解释的很详细,后面的理解均在该图上展开。
要了解Scrapy框架,从这个图出发,理解很轻松.
Scrapy使用记录_第1张图片

1.新建scrapy项目: 
scrapy startproject [项目名]

2.在项目内新建Spider(这里定义了爬取的逻辑和解析网页的规则)
scrapy genspider [Spider名称] [要爬取的域名]

3.启动spider
scrapy crawl [Spider名称]

二、知识点

1.理解Downloader Middleware

位于Downloader(下载器)和Engine(引擎)中间,处理两者之间的请求和响应。

三个核心方法

process_request(request,spider)

process_response(request,response,spider)

process_exception(request,exception,spider)

2.理解Spider Middleware

作用情景
1.在响应(response)内容发送给Spider之前对response进行处理
2.在Spider生成的request发送给Scheduler之前,对request进行处理
3.在Spider生成的Item发送给Item Pipeline之前,对Item进行处理。

四个核心方法

process_spider_input(response,spider)

process_spider_output(response,result,spider)

process_spider_exception(response,exception,spider)

process_start_requests(start_requests,spider)

3.理解Item Pipeline

主要功能
1.清理HTML数据
2.验证爬取数据,检查爬取字段
3.查重并丢弃重复内容
4.将爬取结果保存到数据库

核心方法

open_spider(spider)

close_spider(spider)

from_crawler(cls,crawler)

4.理解yield构建生成器

这篇文章解释的很好,建议看一下:参考资料

简单来说,假如构建一个函数,调用这个函数时不立即执行,而是返回返回一个generate对象,在你需要它执行时主动控制其启动。
generate对象下有__next__这样一个方法,调用它就相当于启动generate(ps:for循环中默认直接启动)

见解:
为什么scrapy需要用到yield来构建生成器对象?
原因:为了各模块之间可以协同操作,共同对同一数据进行处理。假设有A、B、C、D四个模块,数据段X和Y,模块之间协作顺序为ABCD依次进行,ABCD各通过yield产生一个生成器对象。A启动生成器对X进行操作,然后交给B,依次进行。在此期间Y不会被A加载进来,只有等X被处理完毕,A才会启动生成器加载Y,依次对Y进行处理。

就像工厂的流水线上,有很对道工序,一个零件接受完一道工序后才能去下一道工序,生成器就相当于控制流水线转动的开关。

三、实用技巧

1.定制user-agent

方法1

在项目settings.py文件中进行配置
在这里插入图片描述

方法2 (推荐)

借助Downloader Middleware暂且称其为下载中间件

例如设置随机的User-Agent
在项目的middlewares.py文件中添加类RandomUserAgentMiddleware

'''
引用自:https://github.com/Python3WebSpider/ScrapyDownloaderTest/blob/master/scrapydownloadertest/middlewares.py
'''

import random
from scrapy import Request

class RandomUserAgentMiddleware():
    def __init__(self):
        self.user_agents = [
            'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)',
            'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2',
            'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1'
        ]
    
    def process_request(self, request, spider):
        request.headers['User-Agent'] = random.choice(self.user_agents)

settings.py文件中的Downloader Middleware字段内注册这个方法使之生效
在这里插入图片描述

DOWNLOADER_MIDDLEWARES = {
   'scrapydownloadertest.middlewares.RandomUserAgentMiddleware': 543,
}

2.从XHR类型的页面爬取数据

ajax是asynchronous javascript and XML的简写,中文翻译是异步的javascript和XML,这一技术能够向服务器请求额外的数据而无须卸载页面,会带来更好的用户体验

ajax技术的核心是XMLHttpRequest对象(简称XHR)

响应到的数据格式为json

第一步:编写Spider程序

class ImagesSpider(scrapy.Spider):
    name = ''    #Spider名称
    allowed_domains = ['image.so.com']		#Spider作用的域名
    start_urls = ['http://image.so.com/']		#Spider在启动时爬取的url列表

    def start_requests(self):		#用于向网站发起请求
        data={'ch':'photography','listtype':'new',}		#用于构建连接发起ajax请求的参数
        base_url='https://image.so.com/zjl?'		#参数会和base_url组成新的连接用于发起请求
        for page in range(1,self.settings.get('MAX_PAGE')+1):		#控制请求次数,这里设置的MAX_PAGE是50,也就是会发起50次请求
            data['sn']=page*30
            params=urlencode(data)		#将字典转化为url格式的字符串
            print(params)
            url = base_url + params		#合成链接
            yield Request(url, self.parse)	#返回一个生成器对象

    def parse(self, response):
        result = json.loads(response.text)		#将想用内容从json格式转换为python数据格式,这里应该是字典
        for image in result.get('list'):
            item = ImageItem()		#爬虫项目对象
            item['id'] = image.get('id')		#将获取到的字段传递给项目
            item['url'] = image.get('qhimg_url')
            item['title'] = image.get('title')
            item['thumb'] = image.get('qhimg_thumb')
            yield item

第二步:编写Item,定义数据库名,要爬取数据的字段名

class ImageItem(Item):
    collection = table = 'images'		#collection用于Mongodb,table用于mysql

    id = Field()
    url = Field()
    title = Field()
    thumb = Field()

第三步:编写Pipelines
用于将数据写入数据库或下载到本地

#用于将数据写入Mongodb
class MongoPipeline(object):
    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DB')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def process_item(self, item, spider):
        name = item.collection
        self.db[name].insert(dict(item))
        return item

    def close_spider(self, spider):
        self.client.close()

#用于将数据写入mysql
class MysqlPipeline():
    def __init__(self, host, database, user, password, port):
        self.host = host
        self.database = database
        self.user = user
        self.password = password
        self.port = port

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            host=crawler.settings.get('MYSQL_HOST'),
            database=crawler.settings.get('MYSQL_DATABASE'),
            user=crawler.settings.get('MYSQL_USER'),
            password=crawler.settings.get('MYSQL_PASSWORD'),
            port=crawler.settings.get('MYSQL_PORT'),
        )

    def open_spider(self, spider):
        self.db = pymysql.connect(self.host, self.user, 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):
        print(item['title'])
        data = dict(item)
        keys = ', '.join(data.keys())
        values = ', '.join(['%s'] * len(data))
        sql = 'insert into %s (%s) values (%s)' % (item.table, keys, values)
        self.cursor.execute(sql, tuple(data.values()))
        self.db.commit()
        return item
#用于将数据下载到本地
class ImagePipeline(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

    def get_media_requests(self, item, info):
        yield Request(item['url'])

3.Scrapy对接Splash

相较于selenium的优点:
Scrapy对接selenium时需要自己实现Downloader Middleware,而Scrapy-Splash直接提供了多个高效的Downloader Middleware,在需要时直接调用即可。

3.1 添加配置

#Splash运行的服务器地址
SPLASH_URL='http://localhost:8050'

DOWNLOADER_MIDDLEWARES={
'scrapy_splash.SplashCookiesMiddleware':723,
'scrapy_splash.SplashMiddleware':725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware':810
}

SPIDER_MIDDLEWARES={
'scrapy_splash.SplashDeduplicateArgsMiddleware':100,
}

3.2 使用

方法1:直接生成SplashRequest对象并传递相应的参数,Scrapy会将此请求转发给Splash,Splash对页面进行渲染加载,然后再将渲染结果传递回来。此时Response的内容就是渲染完成的页面结果了。

yield SplashRequest(url,self.parse_result,
args={
	..."用于传递渲染参数"
},
endpoint='render.json',
splash_url='',

你可能感兴趣的:(数据处理,分析)