Python 网络爬虫:Scrapy框架下爬虫的简单思路

文章目录



前言

最近写了一个词典网站的爬虫,响应以及获取数据的方式非常的简单,本以为会是个轻松的爬虫脚本,没曾想出了很多意料之外的问题,这样使得我对代码本身有了更加清晰的认知。


提示:以下是本篇文章正文内容,下面案例可供参考

一、分析数据源

在爬取所需的资源时,首当其冲的就是要确认数据来源,数据具体在什么位置,数据的目录页,分类所进入的逻辑是什么等等。笔者在编写一个爬虫脚本之前往往会先思考这个问题,只要将逻辑考虑的清晰顺畅,后面我们所做的代码编写会轻松很多。

目标网站:https://ctext.org/zhs

思路分析

首先进入主页,引入眼帘的是如下页面:
在这里插入图片描述
左侧是网站的目录,我们的目的是拿到该词典的古文双译句子。按照该网站的目录情况,似乎该词典网站的数据大体上分为"先秦两汉"和"汉代之后"两类,我们尝试点进去看看。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意这里url的变化,在进入"先秦两汉"页面后,首先网站的url发生了变化,多了一个/pre-qin-and-han/部分,这次可以初步判定这是一个get请求。并且在切换英文繁体的时候,仅是ulr的后缀发生了变化,而且我们发现在英文和繁体的页面中,会直接带每一句其相应的英文翻译,这样的话,我们最终的目标页就确定了,我们只需要进入该页面就可以拿到我们所需的内容。

再其次我们发现,在"先秦两汉"的页面中,似乎其下的分类,例如:论语,孟子等等都是以及加载好了,似乎我们并不需要经过"论语"这一部分。我们在"先秦两汉"的页面中就可以拿到每一部古文经典中每一篇的url。我们打开F12调试验证一下:
在这里插入图片描述
在这里插入图片描述
果然,验证了之前我们的猜想,可是这里似乎还多了例如:儒家,墨家,道家等这样“先秦两汉”次级目录下的url,并且在这里的页面规则下,我们利用xpath提取相应的href时会包含它们。我们初步的思路是跳过这一次级目录,直接取"儒家,墨家,道家等"其下的每一篇的url,那会不会有影响呢?我们分别点进两种页面看看。

在这里插入图片描述
在这里插入图片描述

答案是不会,即使我们将"先秦两汉"页面中的包含了"论语,孟子"等url提取并访问,但在我们进去真正所需页面,例如"儒家"的次级目录中的"论语"时,编写的xpath才能提取到真正所需的内容,而其他页面获取为空。

截止目前,我们的思路是:先进入"先秦两汉"的页面,再访问"先秦两汉"目录下的所有url,随后在进入到例如:“论语”,"孟子"页面中的每一篇文章的url,这样即可拿到目标资源。

用个实例来理一理:
https://ctext.org/pre-qin-and-han/ens → https://ctext.org/analects/ens → https://ctext.org/analects/xue-er/ens
大致上是这样的顺序。

在访问页面的时候,我发现似乎并不是每一篇文章都有英文翻译,并且我还发现个别文章比我们预想的访问顺序会少一级,如图:
在这里插入图片描述
我们访问"独断"时,按照顺序显示的页面应该是"独断"目录下的每一篇文章的url,但这这里却直接显示出了目标内容,如图:
在这里插入图片描述
这是一个小坑,如果忽略这一点可能会导致我们爬取的数据不全,产生遗漏。所幸通过观察,我发现,带有中英文翻译的页面,在我们访问的第二级,也就是例如:"论语"的页面,"论语"页面中是其下每一篇文章的url,如果该篇文章内含中英文翻译,那该篇文章的url的标题结构就是 中文 + “-” + “英文”,而不带有英文翻译的 标题结构仅是 中文。如图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
似乎逻辑思路已经完善了,但在我编写代码完后发现中英文的数量对不上,中文会比英文的数量要多,这让我非常难受,在仔细排查一番后我发现,问题是出在最后的目标页面上,如图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们发现,如图上这个作为例子,安装思路,我们先进入 https://ctext.org/pre-qin-and-han/ens,然后进入 https://ctext.org/mozi/ens,而在墨子的页面中,其图示路径也不同,如图:
在这里插入图片描述
若是点箭头所指的url,一般是如图下所示的页面:
在这里插入图片描述
但"墨子"的"卷十一"却不同,如图:
在这里插入图片描述
在这里插入图片描述
"卷十一"的内容不是次级url而是目标文本,并且其文本中英文并不是一一对应。这个网站的页面属实有点乱,不过好在确定了思路,下面是代码部分。

二、代码部分

代码如下(示例):

import scrapy
from redis import Redis
from lxml.html import etree
from html import unescape
from scrapy import cmdline, selector, Request

class ClassicalSpider(scrapy.Spider):
    name = 'classical'
    start_urls = ['https://ctext.org/pre-qin-and-han/ens','https://ctext.org/post-han/ens']

    conn = Redis(host='127.0.0.1', encoding='utf-8', port=6379)

    def parse(self, response):

        url_primary = response.xpath('//*[@id="content3"]/a/@href').getall()
        for i in url_primary:
            yield response.follow(url=i, callback=self.parse_detail,dont_filter=True)


    def parse_detail(self, response):

        b = response.body
        page = etree.HTML(b)
        page = unescape(page)

        whole_url = []
        whole_title = []
        Available_url = []
        Available_title = []

        content_chs_list = []
        content_ens_list = []

        for bad in page.xpath('//*[@id="content3"]/table[@border="0"]//td[@class="etext"]/p'):
            bad.getparent().remove(bad)

        for y in page.xpath('//table[@border="0"]'):

            content_chs = [x.xpath('string(.)').strip() for x in y.xpath('.//td[@class="ctext"]') if
                           x.xpath('string(.)').strip()]
            content_ens = [x.xpath('string(.)').strip() for x in y.xpath('.//td[@class="etext"]') if
                           x.xpath('string(.)').strip()]

            if len(content_chs) == len(content_ens):
                for i in content_chs:
                    content_chs_list.append(i.strip())

                for i in content_ens:
                    content_ens_list.append(i.strip())

        content_secondary = response.xpath('//*[@id="content2"]/a/@href').extract()
        for i in content_secondary:
            whole_url.append(i)

        title_primary = response.xpath('//*[@id="content2"]/a/text()').getall()
        for i in title_primary:
            whole_title.append(i)


        for whole_title,whole_url in zip(whole_title,whole_url):
            if whole_title.find('-')>=0:
                Available_url.append(whole_url)
                Available_title.append(whole_title)

        for ture_url in Available_url:
            yield response.follow(url=ture_url, callback=self.parse_page,meta={'chs': content_chs_list,'ens':content_ens_list})


    def parse_page(self, response):

        content = response.body
        page = etree.HTML(content)
        page = unescape(page)

        content_chs_list = response.meta['chs']
        content_ens_list = response.meta['ens']


        for bad in page.xpath('//*[@id="content3"]/table[@border="0"]//td[@class="etext"]/p'):
            bad.getparent().remove(bad)

        for y in page.xpath('//table[@border="0"]'):

            content_chs = [x.xpath('string(.)').strip() for x in y.xpath('.//td[@class="ctext"]') if
                           x.xpath('string(.)').strip()]
            content_ens = [x.xpath('string(.)').strip() for x in y.xpath('.//td[@class="etext"]') if
                           x.xpath('string(.)').strip()]

            if len(content_chs) == len(content_ens):
                for i in content_chs:
                    content_chs_list.append(i.strip())

                for i in content_ens:
                    content_ens_list.append(i.strip())


        if len(content_chs_list)==len(content_ens_list):
            ch_url = (response.url).replace('/ens','')
            yield response.follow(url=ch_url, callback=self.parse_ch,meta={'chs': content_chs_list,'ens':content_ens_list})


    def parse_ch(self, response):
        item = {}

        content_chs_list = response.meta['chs']
        content_ens_list = response.meta['ens']

        content = response.body
        page = etree.HTML(content)
        page = unescape(page)
        content_ch_list = []


        for bad in page.xpath('//*[@id="content3"]/table[@border="0"]//td[@class="etext"]/p'):
            bad.getparent().remove(bad)


        for y in page.xpath('//table[@border="0"]'):

            content_ch = [x.xpath('string(.)').strip() for x in y.xpath('.//td[@class="ctext"]') if
                           x.xpath('string(.)').strip()]
            content_ens = [x.xpath('string(.)').strip() for x in y.xpath('.//td[@class="etext"]') if
                           x.xpath('string(.)').strip()]

            if len(content_ch) == len(content_ens):
                for i in content_ch:
                    content_ch_list.append(i.strip())



        # content_ch = [x.xpath('string(.)') for x in page.xpath('//*[@id="content3"]/table[@border="0"]//td[@class="ctext"]')]
        # for i in content_ch:
        #     if i.strip():
        #         content_ch_list.append(i.strip())

        if len(content_chs_list) == len(content_ens_list) == len(content_ch_list):
            for chs,ch,ens in zip(content_chs_list,content_ch_list,content_ens_list):

                item['chs'] = chs
                item['ch'] = ch
                item['ens'] = ens

                # print(item)
                yield item



if __name__ == '__main__':
    cmdline.execute(["scrapy", "crawl", "classical"])

总结

在完成一个网站代码的编写的同时,多关注逻辑部分以及代码本身,即使是简单的网站,不同代码的执行效率等也是不同的,这正是我们需要学习进步的点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值