python--Scrapy模块的使用


python--Scrapy模块的使用

Scrapy引擎:

用来接收引擎发送过来的请求,压入队列中,并在引擎再次请求时返回,就是在我们所要爬取的url全部放入一个优先队列中,由它来决定下一个处理的url是什么,同时他会自动将重复的url去除

注意:我们在创建一个项目时,在spider中会存在一个start_urls = ['http://dig.chouti.com/'],他将是我们的初始url,会在项目启动后被引擎放入调度器中开始处理

用于下载网页内容,并将网页内容返回给蜘蛛(下载器是基于twisted实现)

Item解析器:

负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

Downloader Middlewares下载器中间件

介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。

Scheduler Middewares调度中间件

1.引擎从调度器中取出一个连接URL,用于接下来的抓取

3.下载器将资源下载,封装为应答包Response

5.解析出实体Item,将实体通过管道解析持久化操作

注意:第一步之前,是先去爬虫start_url中获取初始网址,进行操作

1.创建项目

cd 项目名
scrapy genspider 项目列表名 初始url(后面可以修改)

cd scrapyPro
scrapy genspider chouti chouti.com

3.展示爬虫应用列表

scrapy crawl 爬虫应用名称

scrapy crawl chouti --nolog  #--nolog不打印日志

项目结构

chouti爬虫的编写

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class ScrapyproItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass

class ChoutiItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    href = scrapy.Field()

pipeline.py文件编写:实现数据持久化操作

ITEM_PIPELINES = {
   'scrapyPro.pipelines.ScrapyproPipeline': 300,  #后面300代表优先级
}

补充:Selector的操作

Selector(response=response).xpath('/html/body/ul/li/a/@href').extract()  #提取所有的,是个列表
Selector(response=response).xpath('//body/ul/li/a/@href').extract_first()  #提取第一个

注意:'//'代表子孙标签,'/'代表子标签,另外'./'代表当前标签下寻找

import scrapy,hashlib
from scrapy.selector import Selector,HtmlXPathSelector
from scrapy.http import Request

class ChoutiSpider(scrapy.Spider):
    name = 'chouti'
    allowed_domains = ['chouti.com']
    start_urls = ['http://dig.chouti.com/']
    visited_urls = set()  #用于存放我们获取的url的md5值,而且是集合去重

    def md5(self,url):  #将url转md5
        ha_obj = hashlib.md5()
        ha_obj.update(bytes(url,encoding="utf-8"))
        key = ha_obj.hexdigest()
        return key

    def parse(self, response):
        page_objs = Selector(response=response).xpath("//div[@id='dig_lcpage']//a[@class='ct_pagepa']")  #解析实体
        for page in page_objs:
            href = page.xpath("@href").extract_first()
            ha_href = self.md5(href)
            if ha_href in self.visited_urls:
                pass
            else:
                self.visited_urls.add(ha_href)  #将获取的url添加到集合
                new_url = "https://dig.chouti.com%s"%href
         yield Request(url=new_url,callback=self.parse)  #请求新的url,获取下面的url  对于阻塞操作使用yield切换,实现异步

注意:默认是获取所有的页面100多页,我们可以在setting文件设置解析深度

简单实例应用:获取校花网的图片和姓名,按照姓名进行持久化文件归类

# -*- coding: utf-8 -*-
import scrapy
import hashlib
from scrapy.http.request import Request
from scrapy.selector import Selector,HtmlXPathSelector
import scrapy.http.response.html
from ..items import XiaohuaItem

class XiaohuaSpider(scrapy.Spider):
    name = 'xiaohua'
    allowed_domains = ['xiaohua.com']
    start_urls = ['http://www.xiaohuar.com/']
    visited_url = set()
    visited_url_img = {}
    visited_url_title = {}

    def md5(self,url):
        hash_obj = hashlib.md5()
        hash_obj.update(bytes(url,encoding="utf-8"))
        return hash_obj.hexdigest()

    def parse(self, response):
        #获取首页中所有的人物的下一级url,过滤掉校草
        xh_a = Selector(response=response).xpath("//ul[@class='twoline']/li")
        for xh in xh_a:
            a_url = xh.xpath("./a[@class='xhpic']/@href").extract_first()
            a_title = xh.xpath("./a/span/text()").extract_first()
            if a_title.find("校草") != -1:
                continue
            if not a_url.startswith("http"):
                a_url = "http://www.xiaohuar.com%s" % a_url
            ha_url = self.md5(a_url)
            if ha_url in self.visited_url:
                pass
            else:
                self.visited_url.add(ha_url)
                self.visited_url_title[ha_url]=a_title

                yield scrapy.Request(url=a_url,callback=self.parse,dont_filter=True)

        #下面是所有人物下一级url中去查找所有照片,注意:部分小照片和大照片的区别在于前面多了small,大照片只取前32位即可
        #/d/file/20171202/small062adbed4692d28b77a45e269d8f19031512203361.jpg   小照片
        #/d/file/20171202/     062adbed4692d28b77a45e269d8f1903.jpg             大照片
       
        xh_img = Selector(response=response).xpath("//div[@class='post_entry']")
        xh_img_a = xh_img.xpath("./ul//img/@src")

        if len(xh_img) == 0:
            xh_img = Selector(response=response).xpath("//div[@class='photo-Middle']/div")
            if len(xh_img) != 0:
                xh_img = xh_img[1]
            else:
                xh_img = Selector(response=response).xpath("//div[@class='photo-m']/div")[1]
            xh_img_a = xh_img.xpath("./table//img/@src")
        
        # 上面找到标签,下面开始对标签进行循环,获取所有照片url

        for xh_img_item in xh_img_a:
            xh_img_url = xh_img_item.extract()

            if xh_img_url.find("small"):
                tmp_list = xh_img_url.rsplit("/",1)
                tmp_name_list = tmp_list[1].replace("small","").split(".")
                xh_img_url = "/".join([tmp_list[0],".".join([tmp_name_list[0][:32],tmp_name_list[1]])])
            if not xh_img_url.startswith("http"):
                xh_img_url = "http://www.xiaohuar.com%s" % xh_img_url
            #将所有照片加入字典  url:名字
            self.visited_url_img[xh_img_url] = self.visited_url_title[self.md5(response.url)]
        
        #若是收集完成,那么两者的长度是一致的,开始进行持久化
        if len(set(self.visited_url_img.values())) == len(self.visited_url) and len(self.visited_url) != 0:
            for xh_url in self.visited_url_img.items():
                item_obj = XiaohuaItem(title=xh_url[1],img_url=xh_url[0])
                yield item_obj

items.py

import requests,os

class
XiaohuaPipeline(object): def process_item(self, item, spider): ''' title img_url ''' file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),'upload',item['title']) if not os.path.isdir(file_path): os.makedirs(file_path) response = requests.get(item['img_url'],stream=False) with open(os.path.join(file_path,item['img_url'].rsplit("/",1)[1]),"wb") as fp: fp.write(response.content) return item

settings.py

原因:可能是我在设置allowed_domains允许域名中所设置的域名不是我们所爬取的网站域名

因为在网站中可能有外联,我们只是需要去访问该网站,而不是他的外联网站,所以设置allowed_domains是必须的,可以过滤掉外联的网站,要是希望获取外联网站,我们在该列表中添加上即可。

我们所爬取的网站时xiaohuar.com,而设置的域名是xiaohua.com所以爬取失败,在request中请求被拒绝

1.修改allowed_domains(推荐)

yield scrapy.Request(url=a_url,callback=self.parse,dont_filter=True)

推荐使用第一种: