python爬虫笔记(七):使用Scrapy框架

Scrapy爬虫

一、构建项目

cmd窗口,cd切换路径

scrapy startproject cartoon

进入第一级目录,也就是含有配置文件的一级
scrapy genspider example example.com
这里不需要给出http://头,会自动创建

scrapy genspider driver "http://comic.kukudm.com/comiclist/2491/index.htm"

然后spiders文件里面多了一个叫driver的py文件,里面已经帮我们写好了类的基本框架了

二、shell分析

没有创建项目也可以查看,用于分析网页结构
shell根据response提前初始化了变量 sel 。该selector根据response的类型自动选择最合适的分析规则(XML vs HTML)

scrapy shell "http://comic.kukudm.com/comiclist/2491/index.htm"

之后可以查看网页标签信息,这里打印的是浏览器的所有Elements

response.body

查看包头

response.headers
提取内容

首先我们先提取每个章节的名字和链接
是这样的,我们得到的response,需要转成Selector对象,就像构造BeautifulSoup对象那样。可以指定text,也可以指定response
Selector(self, response=None, text=None, type=None, root=None, _root=None, **kwargs)
但是为了方便,已经以response属性的形式.selector映射了选择器,可以直接对response使用xpath。

response.xpath("//dd/a[1]/text()")
response.xpath("//dd/a[1]/@href")  

这个时候返回的Selector列表,<Selector xpath='//dd/a[1]/text()' data='求你揉一揉吧 序'>
PS: 这是排行榜第一的漫画,我没有别的意思,真的没有。。。
text()表示提取文字内容,我们需要把弄出来,要用到 extract()

response.xpath("//dd/a[1]/text()").extract()
# ['求你揉一揉吧 序', '求你揉一揉吧 1话', ...]
# ['/comiclist/2491/64789/1.htm', '/comiclist/2491/64790/1.htm', ...]

使用ctrl+c退出之前的shell,现在分析章节页面
我们需要得到每个章节的总页数,提取文本信息后使用re()方法,返回匹配的字符串列表,相当于findall
注意这里不能再text()后提取文字,否则成了单独的字符串,不是Selector对象,不能直接使用re方法了。

scrapy shell "http://comic.kukudm.com/comiclist/2491/64789/1.htm"
pages = response.xpath("//td[@valign='top']/text()")[0].re(r"共(\d+)")[0]

接下来获取图片链接,发现结果不太对,仔细一看,前面有javascript动作,真实的链接是动态加载进去的,我们直接提取的是html和css,就不能向我们这样简单提取了。好在JS里有相关链接信息,我们改成提取script标签。负载的动态加载之后有空研究一下。

response.xpath("//td[@valign='top']/img[1]/@src")
# [<Selector xpath="//td[@valign='top']/img[1]/@src" data='/images/t2.gif'>]

抓取第一条JS脚本,提取document.write,找到有用的链接了。
应该就是这条后缀,newkuku/2018/08/07/求你揉一揉吧_第00话/000166C.jpg,对比img的src,server就应该是n5.1whour.com

response.xpath("//td[@valign='top']/script[1]/text()").extract()
# ['\r\ndocument.write("<IMG SRC=\'"+m201304d+"newkuku/2018/08/07/求你揉一揉吧_第00话
# /000166C.jpg\'><span style=\'display:none\'><img src=\'"+m201304d+"newkuku/2018/08
# /07/求你揉一揉吧_第00话/000262D.jpg\'></span>");\r\n']

三、Scrapy编写

1.spider初探

import scrapy

class DriverSpider(scrapy.Spider):
    name = 'driver'
    allowed_domains = ['comic.kukudm.com']
    start_urls = ['http://http://comic.kukudm.com/comiclist/2491/index.htm/']

    def parse(self, response):
        link_urls = response.xpath("//dd/a[1]/@href").extract()
        for each in link_urls:
            print("http://comic.kukudm.com" + each)
  • name:自己定义的内容,在运行工程的时候需要用到的标识;
  • allowed_domains:允许爬虫访问的域名,防止爬虫跑飞。这个域名需要放到列表里;
  • start_urls:开始爬取的url,同样这个url链接也需要放在列表里;
  • def parse(self, response) :请求分析的回调函数,如果不定义start_requests(self),获得的请求直接从这个函数分析;这两个函数是从scrapy.Spider继承下来的,所以会有自动补全。

    在cmd调用我们写好的spider进行爬取
    注意:这个和spider类里的name一致

scrapy crawl driver

这里写图片描述

2.items编写

item呢,跟字典用法差不多。scrapy.Field()创建了Field对象,且没有被赋值,那么就将作为item的键值。在cmd里import items后,可以这样创造一个对象。

>>> pro = items.CarItem(chap_name="nice")
>>> pro
{'chap_name': 'nice'}
>>> pro.fields
{'chap_link': {}, 'chap_name': {}}
>>> pro.items()
ItemsView({'chap_name': 'nice'})
import scrapy

class CarItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    chapter_name = scrapy.Field()
    chapter_url = scrapy.Field()
    img_url = scrapy.Field()
    img_paths = scrapy.Field()

3.settings编写

BOT_NAME = 'cartoon'

SPIDER_MODULES = ['cartoon.spiders']
NEWSPIDER_MODULE = 'cartoon.spiders'
ROBOTSTXT_OBEY = True
# 是否遵守robotstxt协议
IMAGES_STORE = "F:/cartoon"
# 文件存储根路径
DOWNLOAD_DELAY = 0.25
COOKIES_ENABLED = False
ITEM_PIPELINES = {
   'cartoon.pipelines.CartoonPipeline': 1,
}
# 数字大小表示pipeline执行先后顺序,0-1000

4.spider编写

好像有点问题,有空来改,现在忙比赛了。

import scrapy
import re
from car.items import CarItem

class DriverSpider(scrapy.Spider):
    name = 'driver'
    img_server = "http://n5.1whour.com/"
    server_link = "http://comic.kukudm.com"
    allowed_domains = ['comic.kukudm.com']
    start_urls = ['http://comic.kukudm.com/comiclist/2491']
    pattern = re.compile(r'\+"(.+)\'><span')

    def start_requests(self):
        yield scrapy.Request(url=DriverSpider.start_urls[0], callback=self.parse)

    def parse(self, response):
        chap_links = response.xpath("//dd/a[1]/@href").extract()
        chap_names = response.xpath("//dd/a[1]/text()").extract()
        item = CarItem()
        for index in range(len(chap_links)):
            item["chap_name"] = chap_names[index]
            item["chap_link"] = DriverSpider.server_link + chap_links[index]
            yield scrapy.Request(url=item["chap_link"], meta={"item": item}, callback=self.parse_chap)

    def parse_chap(self, response):
        item = response.meta["item"]
        pre_img_url = response.xpath("//td[@valign='top']/script[1]/text()").extract()
        first_img_link = DriverSpider.img_server + DriverSpider.pattern.findall(pre_img_url)[0]
        item["img_url"] = [first_img_link]
        yield item
        page_num = int(response.xpath("//td[@valign='top']/text()")[0].re(r"共(\d+)")[0])
        img_prefix = first_img_link[: first_img_link.rfind("/") + 1]
        for img_order in range(2, page_num + 1):
            behind_img = img_prefix + str(img_order) + ".htm"
            yield scrapy.Request(url=behind_img, meta={"item": item}, callback=self.parse_img)

    def parse_img(self, response):
        item = response.meta["item"]
        item["chap_url"] = response.url
        pre_img_url = response.xpath("//td[@valign='top']/script[1]/text()").extract()
        behind_img_link = DriverSpider.img_server + DriverSpider.pattern.findall(pre_img_url)[0]
        item["img_url"] = [behind_img_link]
        yield item

5.pipeline编写

yield出来的item就到这来处理了。可以手动实现两个函数,跟spider的启动和关闭相关联的。

from car import settings
from scrapy import Request
import requests
import os

class CarPipeline(object):
    def process_item(self, item, spider):
        if "img_url" in item:
            imges = []
            dir_path = "%s/%s" % (settings.IMAGES_STORE, item["chap_name"])
            if not os.path.exists(dir_path):
                os.makedirs(dir_path)

            for img_url in item["img_url"]:
                tail = img_url.split(".")[-1]
                order = item["chap_name"].split("/")[-1].split(".")[0]
                img_file_name = "第" + order + "页" + tail
                file_path = "%s/%s" % (dir_path, img_file_name)
                imges.append(file_path)
                if os.path.exists(file_path):
                    continue
                with open(file_path, "wb") as handle:
                    response = requests.get(img_url)
                    for block in response.iter_content(1024):
                        if not block:
                            break
                        handle.write(block)
            item["img_path"] = imges
        return item

猜你喜欢

转载自blog.csdn.net/weixin_42231070/article/details/82289508