百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

python爬虫案例!初学者的不二之选

toyiye 2024-07-11 00:35 10 浏览 0 评论

项目背景:作为一个标准肥宅,周末看小说成了打发时间的标配,结果逛了几个小时的小说圈,发现居然无书可看,顿时怒从心头起,恶向胆边生,随即写一个爬虫,爬取镇魂小说网的一些小说,诸位看官请坐,且听我一 一道来。

目标网址:https://www.zhenhunxiaoshuo.com

代码思路:分为三步走,

首先:获取到站点分类下的书本链接

其次:请求书籍的链接,获取书籍章节

最后:请求章节内容,保存为txt文件

接下来上菜

代码详细情况如下:

搞python,无法避免的第一味开胃汤,导入需要用到的包

import re         #正则表达式专用
import os         #写入文件
import requests   #发送请求
import traceback  #异常处理
import multiprocessing    #综合处理线程包
import lxml.etree         #解析html
from lxml import etree
from multiprocessing.dummy import Pool  #多线程加速

接下来,开始上主菜,由本大厨为各位顾客介绍一下咱们的菜品,上的第一道菜,是一个初始化方法,它采用__init__方法构造函数,用于初始化示例对象,当创建这个类的新实例时,这个方法就会被自动调用。然后设置实例变量_root_url,用来存储爬虫的基础url。最后分别设置_chunai_path、_yanqing_path、_priest_path三个变量用来作为存放文件的目录。

def __init__(self):
    #设置根URL
    self._root_url = 'https://www.zhenhunxiaoshuo.com'
    #设置头信息
    self._headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                      '(KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.39',
    }
    #设置目标文件存放目录
    self._chunai_path = './纯爱小说类'
    self._yanqing_path = './言情小说类'
    self._priest_path = './priest小说集'

第一道菜虽然味美,但是快乐的时光总是短暂的,接下来,我们在期望的目光中迎来了第二道菜品。

def get_pagination(self, url) -> int:
    '''
    获取当前分类共有多少页
    '''
    response = requests.get(url=url, headers=self._headers)
    response.close()
    tree = etree.HTML(response.text)
    try:
        th_list = tree.xpath('//div[@class="content"]/article/table[2]//tr/th')
        if th_list:
            pagination = len(th_list) - 2
            return pagination
        else:
            return 0
    except Exception as e:
        return 0


看到这里,是不是有一些初学者准备开口骂人了,说好的期望目光呢,结果每个字母都认识,但是连在一起就不认识了。

且听我一一道来,这味菜之所以好,好在哪里?

首先,说明一下,这个函数的主要功能是获取我们需要爬取的数据的页数有多少。request和etree的组合,request请求数据,etree将数据转换成我们熟悉的html格式,然后再通过xpath强大的功能将页数提取出来,在顾客的口中留下与众不同的味道。

接下来的几道菜品,是为了弥补顾客上一道菜吃得不过硬,特意为大家加上的,和上一道异曲同工,request和etree的组合,request请求数据,etree将数据转换成html格式,然后再通过xpath提取数据,然后删除0号位置。

def parse_outline(self, url: str) -> list:
    '''
    tr标签的列表tr_list
    '''
    response = requests.get(url=url, headers=self._headers)
    response.close()
    tree = etree.HTML(response.text)
    tr_list = tree.xpath('//article[@class="article-content"]/table[1]//tr')
    del tr_list[0]
    # 返回tr标签的列表tr_list
    return tr_list

这一类的菜品上齐了,接下来给大家展示一道不一样的菜品,用熟悉的配方,熟悉的调料,制作出的不一样风味的美食。

def get_novel_data(self, tr: lxml.etree) -> tuple:
    '''
    解析tr标签中对应的小说url,小说名字,小说作者
    '''
    try:
        novel_name = tr.xpath('./td/a/strong/text()')[0]
        novel_author = tr.xpath('./td[2]/text()')[0]
        novel_url = self._root_url + tr.xpath('./td/a/@href')[0]
        path_novel = f'{self._chunai_path}/{novel_name}---{novel_author}'
        if not os.path.exists(path_novel):
            os.mkdir(path_novel)
        return path_novel, novel_name, novel_url
    except Exception as e:
        pass

每一行代码眼熟吧!


开局王炸,采用大火将xpath进行烹饪,一开始就进行解析,烹饪出名字、路径以及url,然后创建存储路径,这样,高端的美食往往只需要简单的处理,一道色香味俱全的美食就出炉了。

接下来上一道硬菜,

def get_section_data(self, novel_data: dict) -> None:
    '''
    通过novel_data获取每一章节对应的内容
    '''
    novel_path = novel_data[0]
    novel_url = novel_data[2]

    novel_name = novel_data[1]

    response = requests.get(url=novel_url, headers=self._headers)
    response.close()
    tree = etree.HTML(response.text)
    article_list = tree.xpath('//div[@class="excerpts"]/article')
    for article in article_list:
        section_name = article.xpath('./a/text()')[0]
        section_url = article.xpath('./a/@href')[0]
        try:
            r = re.findall('[¥#%*&@]', section_name)
            for i in r:
                section_name = section_name.replace(i, '')
        except Exception as e:
            pass
        path = novel_path + f'/{section_name}.txt'
        name = novel_name + f'---{section_name}'
        response = requests.get(url=section_url, headers=self._headers)
        response.close()
        tree = etree.HTML(response.text)
        content = tree.xpath('//article[@class="article-content"]//text()')
        content = ''.join(content)
        # 保存
        self.save_data(path=path, name=name, content=content)
    return

怎么样,看到代码吓到了吧。但是,总是有那么几个大哥是喜欢啃硬骨头的,那么接下来,我带大家品尝一下这一味大骨汤。

首先从整体来看这段Python代码定义了一个名为get_section_data的方法,该方法用于从一个小说网站获取小说的每一章节的内容。

然后切块处理,5-8行代码提取小说路径、URL和名称,10-13行代码使用requests库发送GET请求到小说URL,并使用etree.HTML解析返回的HTML内容,使用XPath查询HTML文档,找到所有class为"excerpts"的div元素下的article元素。接下来遍历每一个章节。15-16行代码提取章节名称和URL,17-20行代码表示如果章节名称包含特定的字符(如'¥#%*&@'),则删除这些字符。

23-24行就进入尾声了,构建保存路径和文件名,25行到29行代码表示,发送请求获取章节的内容,提取章节内容,保存章节内容。

最后return结束这道美食。

事实证明,硬菜都是需要细嚼慢咽才能品尝出味道。

下一道是小甜品,作为中场休息补充能量专用。

def save_data(self, path: str, name: str, content: str) -> None:
    '''
    保存数据
    '''
    if not os.path.exists(path):
        with open(file=path, mode='w', encoding='utf-8') as f:
            f.write(content)
        print(f'下载完成---{name}')
    else:
        print(f'已经下载---{name}')
    return

该方法根据提供的pathnamecontent参数,将章节内容保存到指定的文件路径。如果文件已存在,则不会重新写入内容,并打印一条消息表示已经下载。

慢慢的慢慢的,开始渐入佳境,开始回味上面菜品的味道,开始调用上面定义的函数,tr标签,tr标签对应的内容,然后把内容报错。

def spider(self, url: str) -> None:
    '''
    开始爬虫
    '''
    pool = Pool(15)
    # 解析第一页大纲每本小说的tr标签
    tr_list = self.parse_outline(url=url)
    # 通过tr标签解析对应的小说url, 小说名字,小说作者
    novel_data_list = pool.map(self.get_novel_data, tr_list)
    # 获取小说内容并保存
    pool.map(self.get_section_data, novel_data_list)
    return

最后按照类别,将纯爱小说类、言情小说类、priest小说集等批量保存到本地文件中。

def chunai(self):
    if not os.path.exists(self._chunai_path):
        os.mkdir(self._chunai_path)
    url_outline = self._root_url + '/chunai'
    # 获取页数
    pagination = self.get_pagination(url=url_outline)
    if pagination == 0:
        print('纯爱类小说---共1页')
        self.spider(url=url_outline)
    else:
        print(f'纯爱类小说---共{pagination}页')
        # 爬取第一页
        self.spider(url=url_outline)
        # 爬取以后的:
        for i in range(2, pagination + 1):
            page_url = self._root_url + f'/chunai{i}'
            self.spider(url=page_url)

def yanqing(self):
    if not os.path.exists(self._yanqing_path):
        os.mkdir(self._yanqing_path)
    url_outline = self._root_url + '/yanqing'
    # 获取页数
    pagination = self.get_pagination(url=url_outline)
    if pagination == 0:
        print('言情类小说---共1页')
        self.spider(url=url_outline)
    else:
        print(f'言情类小说---共{pagination}页')
        # 爬取第一页
        self.spider(url=url_outline)
        # 爬取以后的:
        for i in range(2, pagination + 1):
            page_url = self._root_url + f'/yanqing{i}'
            self.spider(url=page_url)

def priest(self):
    if not os.path.exists(self._priest_path):
        os.mkdir(self._priest_path)
    url_outline = self._root_url + '/priest'
    # 获取页数
    pagination = self.get_pagination(url=url_outline)
    if pagination == 0:
        print('priest小说集---共1页')
        self.spider(url=url_outline)
    else:
        print(f'priest小说集---共{pagination}页')
        # 爬取第一页
        self.spider(url=url_outline)
        # 爬取以后的:
        for i in range(2, pagination + 1):
            page_url = self._root_url + f'/priest{i}'
            self.spider(url=page_url)

这里不做详细解释,注释已经写好,并且每类的逻辑基本一致。

最后搞一点宵夜。

if __name__ == '__main__':
    try:
        zh = ZhenHun()
        zh.chunai()
        zh.yanqing()
        zh.priest()
        input('全部小说获取完成')
    except Exception as e:
        print(traceback.format_exc())
        input('有异常')

果然在变胖的路上一去不回。


相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码