顶点小说

装xpath helper

GitHub - mic1on/xpath-helper-plus: 这是一个xpath开发者的工具,可以帮助开发者快速的定位网页元素。

Question:加载完插件点击没反应

Answer:将开发人员模式关闭即可

爬虫介绍

分类:

  • 搜索引擎:爬取范围广
  • 聚焦爬虫:爬取范围聚焦

介绍:

程序发起请求(request),获取响应(response),解析response中的数据

URL

即统一资源定位符

组成:

  • 协议

    • http
    • https:新增SSL协议(证书验证),之前花钱,现在开源了,所以大部分网站都是https,据说性能损耗,但忽略不计
  • 主机IP地址(有时也包括端口号)
  • 主机资源具体地址:如目录和文件名等

静态网站和动态网站

  1. 静态网站:数据在页面源代码中
  2. 动态网站:数据在接口中,网站通过ajax请求接口获取数据,再通过js镶在页面中

结构化与非结构化数据

  1. 结构化数据:可以用关系型数据库表示和存储,表现为二维形式的数据
  2. 非结构化数据:数据结构不规则,不方便用二位逻辑来表现,如办公文档、图片、HTML、音频和视频等等

xpath

概念:即为XML路径语言(XML Path Language),用于确定XML文档中某部分位置的语言,python可以使用xpath的语法定位html文档中某部分的位置,并进行抽取

示例:xpath是抽取静态网站数据的常用方法

Question:动态网站可以用xpath进行解析吗?

Answer:

动态网站采用了ajax技术,ajax发起请求对页面进行替换分为 整块替换和 部分替换,部分替换则接口返回数据格式为Json,整块替换则接口返回html文档

如果返回html文档,则可用xpath进行解析,如果返回Json数据,则不可用xpath进行解析

总结:xpath是否可进行解析取决于数据是否为结点数据(是否具有结点),JSON为字符串,肯定不可用xpath进行解析

语法:

  • /:从文档根目录开始选取
  • //:全局进行查找选取,//代表前面有东西,但不重要,相当于正则表达式的 .*?
  • //li/a/text():可以获取小说的所有书名,然后通过python进行切片获取具体的个别数据
  • //li//text():可以获取 li 标签下的所有文字(不区分什么标签),只限深度为1
  • //a[@class="poptext"]:可以选取带有 class属性 的 a标签
  • @href:可以获取此元素的 href属性值
  • *:匹配任何元素结点,如//*、/bookstore/*
  • |:类似and,都获取,如//book/title | //book/price

案例:顶点小说抓取

  1. 导包,定义headers(有的小说网站对headers无要求,有的有要求)
    import requests
    from lxml import etree
    import pymysql # headers
    headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"
    }
  2. 获取小说分类url
    # 获取小说分类url
    def get_type():
    url = "https://www.cdbxs.com/sort/"
    source = requests.get(url=url, headers=headers).content.decode('utf-8')
    href_lists = etree.HTML(source).xpath('//ul[@class="nav"]/li/a/@href')[2:-4]
    type_lists = []
    for href in href_lists:
    type_lists.append(f"{url}{href.split('/')[2]}/1/")
    # print(type_lists)
    return type_lists
  3. 获取最大页
    # 获取最大页
    def get_max_page(first_page_url):
    source = requests.get(url=first_page_url, headers=headers).content.decode('utf-8')
    # print(source)
    max_page = etree.HTML(source).xpath('//a[13]/text()')
    return max_page
  4. 获取每个分类的每一页url
    # 获取小说分类url
    type_lists = get_type()
    # 分类url默认为第一页
    for first_page_url in type_lists:
    # 获取带分类的url的前半截
    type_url = first_page_url.split('1')[0]
    # 获取此分类下最大页
    max_page = get_max_page(first_page_url)
    # 生成此分类下每一页url
    for every_page in range(1, int(max_page[0])+1):
    every_page_url = f"{type_url}{every_page}/"
    print(every_page_url)
  5. 获取小说列表页信息
    def get_book_info(every_page_url):
    source = requests.get(url=every_page_url, headers=headers).content.decode('utf-8')
    book_lists = [] lis = etree.HTML(source).xpath("//ul[@class='txt-list txt-list-row5']/li")
    for li in lis:
    book_id_url = li.xpath("span[@class='s2']/a/@href")[0]
    book_id = book_id_url.split('/')[3]
    # 书名
    book_name = li.xpath("span[@class='s2']/a/text()")[0]
    # 最新章节
    new_chapter = li.xpath("span[@class='s3']/a/text()")[0]
    # 作者
    author = li.xpath("span[@class='s4']/text()")[0]
    # 更新时间
    update_time = li.xpath("span[@class='s5']/text()")[0] source = requests.get(url=f"https://www.cdbxs.com{book_id_url}", headers=headers).content.decode('utf-8')
    # 字数
    font_num = etree.HTML(source).xpath("//p[6]/span/text()")[0]
    # 摘要
    summary = etree.HTML(source).xpath("//div[@class='desc xs-hidden']/text()")[0] # 以元组添加至 book_lists
    # print((book_id, book_name, new_chapter, author, update_time, font_num, summary))
    book_lists.append((book_id, book_name, new_chapter, author, update_time, font_num, summary))
    return book_lists
  6. 获取章节列表url
    # 获取章节列表url
    def get_chapter_urls(chapter_list_url):
    source = requests.get(url=chapter_list_url, headers=headers).content.decode('utf-8')
    # 章节url
    chapter_urls = map(lambda x: "https://www.cdbxs.com" + x, etree.HTML(source).xpath("//div[@class='section-box'][2]/ul[@class='section-list fix']/li/a/@href | //div[@class='section-box'][1]/ul[@class='section-list fix']/li/a/@href")) return chapter_urls
  7. 获取章节详情信息
    # 获取章节详情信息
    def get_chapter_info(chapter_url):
    source = requests.get(url=chapter_url, headers=headers).content.decode('utf-8')
    # 标题
    title = etree.HTML(source).xpath("//h1[@class='title']/text()")
    # 正文
    content = ''.join(etree.HTML(source).xpath("//div[@id='nb_content']/dd//text()"))
    if title:
    return title[0], content
    else:
    return '', content
  8. 整合
    # 获取小说分类url
    type_lists = get_type()
    # 分类url默认为第一页
    for first_page_url in type_lists:
    # 获取带分类的url的前半截
    type_url = first_page_url.split('1')[0]
    # 获取此分类下最大页
    max_page = get_max_page(first_page_url)
    # 生成此分类下每一页url
    for every_page in range(1, int(max_page[0]) + 1):
    every_page_url = f"{type_url}{every_page}/"
    # 获取小说列表页信息
    book_info_lists = get_book_info(every_page_url)
    # 获取章节列表url
    for book_info in book_info_lists:
    print(f"爬取小说:{book_info[1]}...")
    book_id = book_info[0]
    chapter_urls = get_chapter_urls(f"https://www.cdbxs.com/booklist/b/{book_id}/1")
    for chapter_url in chapter_urls:
    # print(chapter_url)
    chapter_info = get_chapter_info(chapter_url)
    print(chapter_info)
    print(chapter_info[0])
    print(chapter_info[1])
    # print(f"title:{chapter_info[0]}")
    # print(f"content:{chapter_info[1]}")

:关注我,后续会陆续出爬虫内容(包括但不仅限于顶点小说进阶:数据入库、多线程多进程爬取数据)

更多精致内容,关注公众号:[CodeRealm]

《从零开始学习Python爬虫:顶点小说全网爬取实战》的更多相关文章

  1. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  2. 利用ssh反向代理以及autossh实现从外网连接内网服务器

    前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...

  3. 外网访问内网Docker容器

    外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...

  4. 外网访问内网SpringBoot

    外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...

  5. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  6. 怎样从外网访问内网Rails

    外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...

  7. 怎样从外网访问内网Memcached数据库

    外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...

  8. 怎样从外网访问内网CouchDB数据库

    外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...

  9. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  10. 怎样从外网访问内网OpenLDAP数据库

    外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...

随机推荐

  1. CentOS7部署Redis(离线单机)

    一.检查是否安装 ## 检查是否安装了Redis [root@localhost /]# ps -ef | grep redis ## 存在就删除 [root@localhost /]# sudo y ...

  2. PageOffice 在线编辑 office文件,回调父页面

    一.子页面调用父页面的方法 var value=window.external.CallParentFunc("ParentFunName(Arguments);");//父页面的 ...

  3. vite.config.js 无法使用__dirname的解决方法

    __dirname 是commonjs规范的内置变量.如果使用了esm 是不会自动注入这个变量的. 在commonjs中,注入了__dirname,__filename, module, export ...

  4. Composer 的下载与安装

    一,Composer 的下载与安装 官网下载:https://getcomposer.org/download/ 打开  penssl的注释,在  D:\wamp\php7230\php.ini  文 ...

  5. Python OpenCV #1 - OpenCV介绍

    一.OpenCV介绍 1.1 OpenCV-Python教程简介 OpenCV由 Gary Bradsky 于1999年在英特尔创立,第一个版本于2000年发布. Vadim Pisarevsky 加 ...

  6. 深入解析HashMap源码

    HashMap是Java程序员使用频率最高的用于映射(键值对)处理的数据类型.随着JDK(Java Developmet Kit)版本的更新,JDK1.8对HashMap底层的实现进行了优化,例如引入 ...

  7. WPF摄像头使用(WPFMediaKit)

    添加WPFMediaKit引用 使用WPFMediaKit操作摄像头需要安装WPFMediaKit相关的Nuget包.选中需要进行摄像头操作的项目,然后通过Nuget安装即可. 页面代码 引入命名空间 ...

  8. .NET桌面程序混合开发之四:键盘事件的响应

    1. 问题 在生产环境中,有一些场景需要窗体来响应键盘事件(注意,是窗体响应,而不是窗体上的控件响应),如解析扫码枪的扫描结果.但在嵌入WebView2的Form程序,Host Form无法对键盘事件 ...

  9. EF CORE 命令行

    EF 命令行 使用管理台模式 Add-Migration 添加一个新的迁移(名词),string是迁移的名称remove-Migration 删除上一次的迁移Update-Database 更新最近一 ...

  10. switch的穿透

      // switch 的 穿透         // 什么是switch的穿透         // 如果在 switch 中没有定义break , switch 会从定位的程序,一直执行到所有sw ...