wananzuiqingfen 发表于 2020-11-22 17:58:18

CrawlSpider全站爬取某君cos妹子图

本帖最后由 wananzuiqingfen 于 2020-11-22 22:38 编辑


介绍加入鱼C大家庭已经半年了,没啥好表示的,既然大家都和我一样喜欢妹子{:10_281:} ,那我也得表示表示了。

目标:全站爬取cos君本子板块妹子图
{:10_281:} : https://www.cosjun.cn/benzi桜桃喵 蕾姆紫睡裙 镇楼:


全套服务{:10_281:} :


教程


1、提取所有页面链接
首先,使用如下两行基于终端的命令创建一个基于 CrawlSpider 的爬虫项目:
scrapy startproject CosJunPro
scrapy genspider -t crawl cos xxoo.com接着,我们来到目标链接{:10_281:}{:10_281:}{:10_281:}: https://www.cosjun.cn/benzi


F12 打开开发者工具,页面拖动到最底部,选择任意分页按钮,观察其结构,可以发现,每一页的链接包含在 li 标签中,而 li 标签又被包含在一个 ul 标签中,并且它的 class 属性为 "page-numbers":




首先,我们需要提取这些主页面的链接,我们使用 Xpath 来解析源码,修改默认生成的爬虫文件如下:
# cos.py
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class CosSpider(CrawlSpider):
    name = 'cos'
    # 我们不禁止爬取的域名
    # allowed_domains = ['xxoo.com']
    start_urls = ['https://www.cosjun.cn/benzi']

    rules = (
      # 提取所有主页面的链接并访问
      Rule(LinkExtractor(restrict_xpaths='//ul[@class="page-numbers"]'), callback='parse_item', follow=True),
    )

    def parse_item(self, response):
      # 作为演示,我们检查响应状态
      print(response)
我们定义了一个关于主页面链接提取并自动请求的 Rule 对象,设置了回调,以及 follow 设置为 True,我们希望链接提取器继续作用于这些被提取到的页面。

在回调函数 parse_item 中,为了演示结果,我们打印响应对象。


2、提取所有详情页链接
接着,我们需要提取所用妹子专辑详情页的链接{:10_281:}

通过开发者工具,任意选中一部专辑,观察其网页结构,可以发现,每张专辑都包含在一个 div 标签中,而这些 div 标签又包含在一个 div 标签中,且它的 class 属性为 "row" 和 "posts-wrapper":



这里虽然涉及到属性多值匹配,但楼主测试的时候使用 contains 不起作用,反倒是直接整个拷贝过去能行{:10_264:} 。

我们更新 rolus,添加一个关于详情页的 Rule :

rules = (
    # 提取所有主页面的链接并访问
    Rule(LinkExtractor(restrict_xpaths='//ul[@class="page-numbers"]'), callback='parse_item', follow=True),
    # 提取详情页的链接并访问
    Rule(LinkExtractor(restrict_xpaths='//div[@class="row posts-wrapper"]'), callback='parse_detaill',follow=False),
)
对详情页的请求得到响应后,我们希望调用 parse_detail 回调函数来处理,我们将会在后面编写这个函数, follow 设置为 False 是因为链接提取器没有必要再作用于详情页中了。


3、解析详情页我们点击任意专辑,进入妹子写真详情页{:10_281:}。

友情提示: 提供给游客的图片只有6张,但是登陆之后会提供全部图片,此外,针对普通用户,只开放部分免费写真全集,大部分有 vip 权限的专辑对普通用户同样只开放6张图片,不过已经够用了{:10_254:}




打开开发者工具,检索图片的链接地址,其网页结构如下所示,每张图片的链接包含在一个 figure 标签中,这些标签又包含在一个 div 标签中,且它有一个唯一的 id 属性值 "gallery-1"


注意,在登录状态下,如果某个专辑是免费的,那么登录后解锁的第二部分将会加载,它的数据结构和上述类似,唯一不同的是div 标签的唯一的 id 属性值变成了 "gallery-2":




由于免费专辑的数量感人,因此是否实现携带 Cookies任君抉择,我将 Cookies 的设置以及免费生成 Cos 君账号的问题作为了一个单独的小节,如有需要,请点击上方目录跳转查看。

由上述示例图片,我们已经知道了提取图片链接的思路: 首先定位figure 标签,在定位 div标签,接着定位 a标签提取到图片的链接地址。

此外,我们应该提取专辑的名称作为某一部专辑存储的目录名称,此外,我们还可以提取妹子的名称作为存储关于该妹子的目录名{:10_281:}。
专辑标题的网页结构如下,额外的,我们可以在提取到标题后,继续提取出妹子的名字{:10_281:}:



提取标题的思路为:利用 class 属性值 "entry-header" 定位到 header 标签,在定位到下面的 h1 标签,提取出文本数据即可。

上述的全部工作将由 parse_detail 完成,在编写 parse_detail 之前,我们在 items.py 文件中需要为图片链接和图片标题、妹子名称创建 Item 字段:

# items.py
import scrapy

class CosproItem(scrapy.Item):
    # 图片链接、专辑标题、妹子名称
    img_src = scrapy.Field()
    img_title = scrapy.Field()
    sister_name = scrapy.Field()
接着,我们将写好的 CosproItem 导入到 cos.py 文件中,编写我们的 parse_detail:
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from CosJunPro.items import CosproItem


class CosSpider(CrawlSpider):
    name = 'cos'
    # 我们不禁止爬取的域名
    # allowed_domains = ['xxoo.com']
    start_urls = ['https://www.cosjun.cn/benzi']

    rules = (
      # 提取所有主页面的链接并访问
      Rule(LinkExtractor(restrict_xpaths='//ul[@class="page-numbers"]'), callback='parse_item', follow=False),
      # 提取详情页的链接并访问
      Rule(LinkExtractor(restrict_xpaths='//div[@class="row posts-wrapper"]'), callback='parse_detail',follow=False),
    )

    def parse_item(self, response):
      # 作为演示,我们检查响应状态
      print(response)

    def parse_detail(self,response):
      print(response)

      # 所有图片的figure标签的序列,用可能存在的第二部分扩展这个序列
      figure_list = response.xpath('//div[@id="gallery-1"]/figure')
      list2 = response.xpath('//div[@id="gallery-2"]/figure')
      figure_list.extend(list2)

      # 本子的标题和妹子的名称
      img_title = response.xpath('//header[@class="entry-header"]//h1/text()').extract_first()
      sister_name = img_title.split(' ')

      for figure in figure_list:
            # 详情页每张图片的url
            img_src = figure.xpath('./div/a/@href').extract_first()

            # 将数据封装到容器中
            item = CosproItem()
            item['img_title'] = img_title
            item['img_src'] = img_src
            item['sister_name'] = sister_name

            yield item

4、管道
Scrapy 提供非常方便的用于文件下载或图片下载的 ImagesPipeline 管道类,我们将基于这个管道类来下载图片:

import scrapy
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline


class CosjunproPipeline(ImagesPipeline):

    def get_media_requests(self, item, info):
      # 手动发起请求
      yield scrapy.Request(item['img_src'], meta={'item': item})

    def file_path(self, request, response=None, info=None, *, item=None):
      item = request.meta['item']
      # 文件路径
      ImgName = f'/{item["sister_name"]}/{item["img_title"]}/' + request.url.split('/')[-1]

      return ImgName

    def item_completed(self, results, item, info):
      img_urls = for ok, x in results if ok]

      if not img_urls:
            raise DropItem('Item download failure')
      else:
            for url in img_urls:
                print(item['img_title'], '下载成功: ', url)

      return item
我们在 get_media_requests 方法中手动发起请求,并传递 Item 对象,

这么做是为了在 file_path 方法中,构建文件名时,我们使用妹子名作为一级目录,关于这个妹子的某一部写着作为二级目录,所有的图片存储在对应的写真目录中。

在 item_completed 方法中,我们检测请求的结果,results 是一个列表,请求的结果作为一个元组存储在其中,如下所示:
[(True, {
    'url': 'https://www.cosjun.cn/wp-content/uploads/2020/11/1605443477-f55cf0d2d7e653c.jpg',
    'path': './test.jpg',
    'checksum': '89758c526d5db6541f2c9466691f5c61',
    'status': 'uptodate'}),
元组的第一个元素对应请求的结果,第二个元素是一个字典,其中存储了某些信息,我们通过一个列表推导式提取出字典的 url 键值。

如果这个列表是空的,意味着我们下载失败了,那么就抛出 DropItem 异常,停止处理这个 Item。反之,我们打印消息表示成功下载。

接着,我们需要在配置文件开启管道,以及定义基于 ImagePipeline 管道的存储路径:

# settings.py
ITEM_PIPELINES = {
   'CosJunPro.pipelines.CosjunproPipeline': 300,
}

IMAGES_STORE = '../COS_Test'

5、中间件
为了避免 ip 被封禁而导致爬取失败,我们将在下载中间件中设置随机 UA、使用代理、处理异常的请求:
# middlewares.py
from fake_useragent import UserAgent               

from Xiaoming.zhima_proxy import get_proxy_pool   


class CosjunproDownloaderMiddleware:
    # 用于设置随机UA标识
    ua = UserAgent()
   
    PROXY_https = get_proxy_pool('https', 20)   # https 协议代理ip

    def process_request(self, request, spider):
      request.headers['User-Agent'] = self.ua.random

      # 为初次请求使用代理
      if request.url == spider.start_urls:
            request.meta['proxy'] = 'https://' + self.PROXY_https.pop()

      return None

    def process_exception(self, request, exception, spider):
      # 为异常的请求更换ip
      request.meta['proxy'] = 'https://' + self.PROXY_https.pop()
      return request
有关于 get_proxy_pool 函数详见白嫖高匿 ip 小节,这里不多做赘述,其具体作用就是接受协议头以及请求的代理 ip 个数这两个参数,来返回一个对应的代理 ip 列表。

关于 fake_useragent 模块UserAgent 类,它是一个用于生成随机UA的很好的实现,每次调用其实例化对象的 random 就可以得到一个随机的 UA 标识。

接着我们需要在 settings.py 文件中开启下载中间件,以及设置一些其他的配置:
DOWNLOADER_MIDDLEWARES = {
   'CosJunPro.middlewares.CosjunproDownloaderMiddleware': 543,
}
# 是否遵从robots.txt协议
ROBOTSTXT_OBEY = False

# 设置日志等级,使输出更为简洁
LOG_LEVEL = 'ERROR'
最后,运行我们的爬虫程序即可:scrapy crawl cos
输出结果如下:



文件目录如我们预期的一样被创建,以妹子名作为一级目录,有关于该妹子的写真作为二级目录,其中存储了这一部写真的图片:





第一次发帖没经验,思来想去不知道要不要做成教程贴,感觉这些东西都是很基本的,我要是唧唧歪歪列一堆大家都知道的东西显得有点没意义,索引就当成爬妹子图乐呵乐呵。

如果您是新手,有关于上述代码不懂的地方可以@我,能力范围内,我会尽量解答您的问题{:10_264:}

{:10_281:}{:10_281:}{:10_281:} 喜欢的话给我点个赞吧~



源码

1、爬虫文件 cos.py
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from CosJunPro.items import CosproItem


class CosSpider(CrawlSpider):
    name = 'cos'
    # 我们不禁止爬取的域名
    # allowed_domains = ['xxoo.com']
    start_urls = ['https://www.cosjun.cn/benzi']

    rules = (
      # 提取所有主页面的链接并访问
      Rule(LinkExtractor(restrict_xpaths='//ul[@class="page-numbers"]'), callback='parse_item', follow=False),
      # 提取详情页的链接并访问
      Rule(LinkExtractor(restrict_xpaths='//div[@class="row posts-wrapper"]'), callback='parse_detail',follow=False),
    )

    def parse_item(self, response):
      # 作为演示,我们检查响应状态
      print(response)

    def parse_detail(self,response):
      print(response)

      # 所有图片的figure标签的序列,用可能存在的第二部分扩展这个序列
      figure_list = response.xpath('//div[@id="gallery-1"]/figure')
      list2 = response.xpath('//div[@id="gallery-2"]/figure')
      figure_list.extend(list2)

      # 本子的标题和妹子的名称
      img_title = response.xpath('//header[@class="entry-header"]//h1/text()').extract_first()
      sister_name = img_title.split(' ')

      for figure in figure_list:
            # 详情页每张图片的url
            img_src = figure.xpath('./div/a/@href').extract_first()

            # 将数据封装到容器中
            item = CosproItem()
            item['img_title'] = img_title
            item['img_src'] = img_src
            item['sister_name'] = sister_name

            yield item




2、items.py
import scrapy


class CosproItem(scrapy.Item):
    # 图片链接、专辑标题、妹子名称
    img_src = scrapy.Field()
    img_title = scrapy.Field()
    sister_name = scrapy.Field()

3、middlewares.py
from fake_useragent import UserAgent

from Xiaoming.zhima_proxy import get_proxy_pool   


class CosjunproDownloaderMiddleware:
    # 用于设置随机UA标识
    ua = UserAgent()

    PROXY_https = get_proxy_pool('https', 20)   # https 协议代理ip

    def process_request(self, request, spider):
      request.headers['User-Agent'] = self.ua.random

      # 为初次请求使用代理
      if request.url == spider.start_urls:
            request.meta['proxy'] = 'https://' + self.PROXY_https.pop()

      return None

    def process_exception(self, request, exception, spider):
      # 为异常的请求更换ip
      request.meta['proxy'] = 'https://' + self.PROXY_https.pop()
      return request

4、pipeline.py
import scrapy
from scrapy.exceptions import DropItem
from scrapy.pipelines.images import ImagesPipeline


class CosjunproPipeline(ImagesPipeline):

    def get_media_requests(self, item, info):
      # 手动发起请求
      yield scrapy.Request(item['img_src'], meta={'item': item})

    def file_path(self, request, response=None, info=None, *, item=None):
      item = request.meta['item']
      # 文件路径
      ImgName = f'/{item["sister_name"]}/{item["img_title"]}/' + request.url.split('/')[-1]

      return ImgName

    def item_completed(self, results, item, info):
      img_urls = for ok, x in results if ok]

      if not img_urls:
            raise DropItem('Item download failure')
      else:
            for url in img_urls:
                print(item['img_title'], '下载成功: ', url)

      return item
5、settings.py
# Scrapy settings for CosJunPro project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#   https://docs.scrapy.org/en/latest/topics/settings.html
#   https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#   https://docs.scrapy.org/en/latest/topics/spider-middleware.html

BOT_NAME = 'CosJunPro'

SPIDER_MODULES = ['CosJunPro.spiders']
NEWSPIDER_MODULE = 'CosJunPro.spiders'


# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'CosJunPro (+http://www.yourdomain.com)'

# Obey robots.txt rules
#ROBOTSTXT_OBEY = True
# 是否遵从robots.txt协议
ROBOTSTXT_OBEY = False

# 日志信息
LOG_LEVEL = 'ERROR'

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)
#COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False

# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}

# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    'CosJunPro.middlewares.CosjunproSpiderMiddleware': 543,
#}

# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
   'CosJunPro.middlewares.CosjunproDownloaderMiddleware': 543,
}

# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    'scrapy.extensions.telnet.TelnetConsole': None,
#}

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'CosJunPro.pipelines.CosjunproPipeline': 300,
}

IMAGES_STORE = '../COS_Test'

# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False

# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'




白嫖高匿ip

爬虫的世界怎么能少的了代理 ip 呢?

不装了,我摊牌了,我是芝麻代理50年铁粉,当他们的公司还没有创建时,我冥冥之中就感觉他们一定能有大作为,唉,这该死的缘分呀~

为什么那么多代理 ip 服务商,我只推荐芝麻? 嘿嘿,那当然是因为它家可以白嫖呀{:10_254:} 。

首先,我们来到官网注册一个账号: http://h.zhimaruanjian.com/

接着在导航栏点击获取API,跳转到代理 ip 的 API 链接生成页面,每天可以领取20个免费的高匿 ip:


注意: 新注册的账号需要找客服帮你解开限制。

接下来,我们生成一个代理 ip 的 API 链接,在这里,我们没有更改任何的配置选项,

根据你的实际需求勾选的(它和 API 链接的参数是对应的,更改 API 参数也可以做到修改配置,官网有详细的参数对应的配置表,可以自行查看):



点击生成 API 链接,从直连IP、独享IP、隧道IP随意选择一个 API 链接复制即可。

我们用 Python 来请求这个 API 链接:

import requests

api_url = '复制的api链接'

res = requests.get(api_url)

res.text


我们得到的响应结果为: '123.97.109.156:4257\r\n'

如何测试这个 ip 的质量呢?官方提供了一个 ip 检测工具,我们用这个测试即可: http://h.zhimaruanjian.com/agent/

我们提取到的这个 ip 的检测结果为:



除了白嫖,芝麻还提供按次购买的服务,对于刚入门需求量较小的用户是一个不错的选择。


最后,我需要发誓我不是芝麻的托,不然我的弟弟一辈子抬不起头{:10_327:}





解决账号问题

如果你要丧心病狂的使用 Cookies 池进行爬取,那么困扰你的一定是账号的问题,因为 Cos 君是基于邮箱注册账号的。

所以,我们只需要在网上找一个提供邮箱代收的站点即可,随用随取: https://www.linshiyouxiang.net/




















笨鸟学飞 发表于 2020-11-22 19:08:41

我只对高匿ip感兴趣{:10_285:}

wananzuiqingfen 发表于 2020-11-22 19:11:28

笨鸟学飞 发表于 2020-11-22 19:08
我只对高匿ip感兴趣

卧槽,我都没编辑完就有人了? 我现在还在解决图片排版问题,高匿ip去芝麻代理就可以了,每天免费20个
芝麻: http://h.zhimaruanjian.com/pay/

wananzuiqingfen 发表于 2020-11-22 22:32:51

笨鸟学飞 发表于 2020-11-22 19:08
我只对高匿ip感兴趣

代理 ip 弄好了,你可以看看{:10_264:}

万恶终极 发表于 2020-11-24 22:45:35

纯学习{:5_91:}
页: [1]
查看完整版本: CrawlSpider全站爬取某君cos妹子图