Scrapy 框架

1. 基本使用

1.1 定义及安装

  • 为了爬取网站数据,提取结构性数据而编写的应用框架
  • scrapy组件工作流程
引擎首先会将爬虫文件中的起始url获取,并且提交到调度器中。如果需要从url中下载数据,则调度器会将url通过引擎提交给下载器,
下载器根据url去下载指定内容(响应体)。下载好的数据会通过引擎移交给爬虫文件,爬虫文件可以将下载的数据进行指定格式的解析。
如果解析出的数据需要进行持久化存储,则爬虫文件会将解析好的数据通过引擎移交给管道进行持久化存储
  • 安装
Linux:
    pip3 install scrapy
 
Windows:
    a. pip3 install wheel
    b. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
    c. 进入下载目录,执行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl
    d. pip3 install pywin32
    e. pip3 install scrapy

1.2 项目创建

1、创建项目:scrapy startproject 项目名称

2、目录文件功能
scrapy.cfg   项目的主配置信息。(真正爬虫相关的配置信息在settings.py文件中)
items.py     设置数据存储模板,用于结构化数据,如:Django的Model
pipelines    数据持久化处理
settings.py  配置文件,如:递归的层数、并发数,延迟下载等
middlewares  中间件设置
spiders      爬虫目录,如:创建文件,编写爬虫解析规则

3、创建爬虫应用程序
(1)cd project_name(进入项目目录)
(2)scrapy genspider 应用名称 爬取网页的起始url (例如:scrapy genspider qiubai www.qiushibaike.com)
自动生成py文件:
import scrapy
class FirsttestSpider(scrapy.Spider):#爬虫类是基于Spider这个父类
    name = 'firstTest' #爬虫文件的名称
    #允许的域名:
    #allowed_domains = ['www.qiushibaike.com']
    #起始url列表:当前的爬虫文件可以从该列表中存储的url中爬取数据
    start_urls = ['https://www.qiushibaike.com/text/']

    #继承与父类的方法:用于解析请求到的页面数据
    def parse(self, response):
        print('haha this is  hehe')
        print(response)
        #xpath函数的返回值是Selector对象(使用xpath表达式解析出来的内容是被存储在了Selecotr对象中)
        #title = response.xpath('//*[@id="qiushi_tag_121076506"]/div[1]/a[2]/h2/text()')[0].extract()
        #extract系列函数的作用是将Selector中的文本数据取出。
        #Selector对象可以直接调用xpath函数。
        title = response.xpath('//*[@id="qiushi_tag_121056271"]/div[1]/a[2]/h2/text()').extract_first()
        title = response.xpath('//*[@id="qiushi_tag_121056271"]/div[1]/a[2]/h2/text()')[0]
        print(title)

(3)scrapy crawl  应用名称  #执行程序

4、基本配置
settings文件:
(1)伪装请求载体身份
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
(2)忽略robots协议
ROBOTSTXT_OBEY = False
(3)开启管道(后面数字指定优先级)
ITEM_PIPELINES = {
   'firstBlood.pipelines.FirstbloodPipeline': 300,
}
(4)使用代理ip时中间件设置
SPIDER_MIDDLEWARES = {
   'firstBlood.middlewares.FirstbloodSpiderMiddleware': 543,
}
# 把默认的该成自己定义的类名 :FirstbloodSpiderMiddleware 改成 MyDaili
----------------------------------
注:在middlewares文件写入:
class MyDaili(object):
    def process_request(self,request,spider):
        #在该方法中设置代理ip
        request.meta['proxy']='http://120.76.231.27:3128'

1.3 爬取糗百作者和标题(get请求)

spiders文件夹(qiutu.py):
import scrapy
from qiubai_all.items import QiubaiAllItem
class QiutuSpider(scrapy.Spider):
    name = 'qiutu'
    #allowed_domains = ['www.qiushibaike.com/pic/']
    start_urls = ['http://www.qiushibaike.com/pic/']

    #指定一个页码通用的url
    url = 'https://www.qiushibaike.com/pic/page/%d/?s=5127014'
    pageNum = 1 #控制页码

    def parse(self, response):
        div_list = response.xpath('//*[@id="content-left"]/div')
        for div in div_list:
            item = QiubaiAllItem() #创建item对象
            #使用管道进行持久化存储的流程:
                #1.使用爬虫文件获得解析到的数据
                #2.将解析到的数据封装存储到items对象中
                #3.将封装好的items对象提交给管道文件
                #4.在管道文件中编写持久化存储的代码
                #5.在settings中开启管道
            item['author']=div.xpath('.//h2/text()').extract_first()
            item['content']=div.xpath('.//div[@class="content"]/span/text()').extract_first()

            yield item

        #想要对应不同页码的url发起请求
        if self.pageNum <= 35:
            self.pageNum += 1 #从第二页发起请求

            print('开始爬取第%d页的数据'%self.pageNum)
            new_url = format(self.url%self.pageNum) #是新页码的url
            #发起请求
            #url:请求的url
            #callback:回调函数(指定解析数据的规则)
            yield scrapy.Request(url=new_url,callback=self.parse)
            
items文件:
import scrapy
class QiubaiAllItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    author = scrapy.Field()
    content = scrapy.Field()
    
pipelines文件:
class QiubaiAllPipeline(object):
    def __init__(self):
        self.fp = None
    def open_spider(self,spider):
        print('开始爬虫')
        self.fp = open('./data.txt','w',encoding='utf-8')

    def process_item(self, item, spider):
        #1.获取item中的数据
        self.fp.write(item['author']+':'+item['content']+'\n\n')
        return item
    def close_spider(self,spider):
        print('爬虫结束')
        self.fp.close()

1.4 校花网图片链接及图片名爬取

spiders文件:
import scrapy
from xiaohuaPro.items import XiaohuaproItem
class XiaohuaSpider(scrapy.Spider):
    name = 'xiaohua'
    #allowed_domains = ['www.521609.com/daxuemeinv']
    start_urls = ['http://www.521609.com/daxuemeinv/']
    url = 'http://www.521609.com/daxuemeinv/list8%d.html'
    page_num = 1
    def parse(self, response):
        li_list = response.xpath('//*[@id="content"]/div[2]/div[2]/ul/li')
        for li in li_list:
            item = XiaohuaproItem()
            item['img_url'] = li.xpath('./a/img/@src').extract_first()
            #拼接图片下载的url
            item['img_url'] = 'http://www.521609.com'+item['img_url']
            item['img_name'] = li.xpath('./a/img/@alt').extract_first()

            yield item
        if self.page_num <= 23:
            self.page_num += 1
            url = format(self.url%self.page_num)
            yield scrapy.Request(url=url,callback=self.parse)

items文件:
import scrapy
class XiaohuaproItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    img_url = scrapy.Field()
    img_name = scrapy.Field()
    
pipelines文件:
import json
import urllib.request
import os
class XiaohuaproPipeline(object):
    def __init__(self):
        self.fp = None
    def open_spider(self,spider):
        print('开始爬虫')
        self.fp = open('./data.json','w',encoding='utf-8')
    def process_item(self, item, spider):
        img_dic = {
            'img_url':item['img_url'],
            'img_name':item['img_name']
        }
        #json.dumps 序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False
        json_string = json.dumps(img_dic,ensure_ascii=False)
        self.fp.write(json_string)

        #下载图片操作
        if not os.path.exists('xiaohua'):
            os.mkdir('xiaohua')
        filePath='xiaohua/'+item['img_name']+'.png'
        urllib.request.urlretrieve(url=item['img_url'],filename=filePath)
        print(filePath+ ':下载成功')

        return item
    def close_spider(self,spider):
        self.fp.close()
        print('爬虫结束')

 #urlretrieve(url, filename=None, reporthook=None, data=None)   有四个参数
 #前两个是下载的地址和保存的路径
 #'参数 reporthook 是一个回调函数,当连接上服务器、以及相应的数据块传输完毕时会触发该回调,我们可以利用这个回调函数来显示当前的下载进度。' \
 # '参数 data 指 post 到服务器的数据,该方法返回一个包含两个元素的(filename, headers)元组,filename 表示保存到本地的路径,header 表示服务器的响应头。'

1.5 百度翻译post请求案例

spiders文件:
import scrapy
class PostdemoSpider(scrapy.Spider):
    name = 'postDemo'
    allowed_domains = ['https://fanyi.baidu.com/sug']
    #start_urls = ['http://https://fanyi.baidu.com/sug/']

    #该方法就是对start_urls列表中的url发起请求
    #def start_requests(self):  #父类对该方法的默认实现
        #for url in self.start_urls:
            #yield scrapy.Request(url=url,callback=parse)

    #重写父类的start_requests方法:该方法默认发起的是get请求
    def start_requests(self):
        post_url = 'https://fanyi.baidu.com/sug/'
        data = {
            'kw':'dog'
        }
        #FormRequest该函数用来发起post请求
        #Request使用用来发起get请求
        yield scrapy.FormRequest(url=post_url,formdata=data,callback=self.parse)

    def parse(self, response):
        print(response.text)
        
pipeline文件:
class PostproPipeline(object):
    def process_item(self, item, spider):
        return item

1.6 获取二级子页面内容(请求传参)

import scrapy
from moviePro.items import MovieproItem
class MovieSpider(scrapy.Spider):
    name = 'movie'
    #allowed_domains = ['www.id97.com']
    start_urls = ['http://www.id97.com/']

    #该方法是用来指定二级子页面的解析规则
    def secondPageParse(self,response):
        #接收Request函数传递过来的参数(meta对应字典的key值),此时得到的item对象即使parse函数中的item= MovieproItem()
        # 再去item文件中加上二级子页面的两个属性(actor,show_time)
        item = response.meta['item']
        item['actor'] = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[1]/td[2]/a/text()').extract_first()
        item['show_time'] = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[7]/td[2]/text()').extract_first()

        #将item提交给管道
        yield item

    def parse(self, response):
        div_list = response.xpath('/html/body/div[1]/div[2]/div[1]/div/div')
        for div in div_list:
            item = MovieproItem()
            item['name'] = div.xpath('./div/div[@class="meta"]//a/text()').extract_first()
            item['kind'] = div.xpath('./div/div[@class="meta"]/div[@class="otherinfo"]//text()').extract()
            item['kind'] = ''.join(item['kind'])
            item['url'] = div.xpath('./div/div[@class="meta"]//a/@href').extract_first()
            #meta={'item':item} 该参数是用来给回调函数进行传值,指定字典的形式,通过key 传多个值
            yield scrapy.Request(url=item['url'],callback=self.secondPageParse,meta={'item':item})
posted @ 2018-10-15 20:48  朝朝哥  阅读(240)  评论(0编辑  收藏  举报