案例简述

主要通过简单的对HTML网页源码分析和对urllib库的简单使用,实现从网页上爬取【文本类型】数据,如本案例当中的爬取笔趣阁小说。

注意点:

  • 本案例只适合网页爬取文本数据的简单练习,不适合实战应用;
  • 本案例没有解决访问频率过于频繁,被爬取网站会限制ip的问题;
  • 针对访问频率频繁问题,本案例采用的是通过延长访问的间隔时间来避免;
  • 本案例的数据存储为本地txt文本文档存储

实现原理

  1. 访问电子书的目录页面,通过分析页面源代码,通过re正则表达式匹配内容,来爬取电子书的章节文本和章节链接;
  2. 分别访问对应的章节链接,继续分析页面源代码,通过re正则表达式匹配内容,爬取电子书的正文文本;
  3. 存储到本地文本文档中。

源代码

import gzip
import random
import re
import time

import urllib.request

import urllib
from urllib3.packages.six import BytesIO


# 笔趣阁小说http://www.biquge.info/

def getNovelList_biquge():
    # text_url = input('请输入电子书网页:')
    # 该链接为笔趣阁《遮天》的访问链接
    text_url = 'http://www.biquge.info/10_10233/'
    # 由于读取的源码是压缩过的十六进制的形式,所以需要转换格式
    html = urllib.request.urlopen(text_url).read()
    bytes_html = BytesIO(html)
    html = gzip.GzipFile(fileobj=bytes_html).read().decode('utf-8')
    # 匹配例子<dd><a href="5103237.html" title="写在连载前">写在连载前</a></dd>
    # 匹配章节的链接,获取链接集合,获取小说名字
    html_reg = r'<dd><a href="(.*?)" title=".*?">(.*?)</a></dd>'
    title_reg = r'<h1>(.*?)</h1>'
    html_reg = re.compile(html_reg)
    title_reg = re.compile(title_reg)
    html_urls = re.findall(html_reg, html)
    title = re.findall(title_reg, html)
    # 章节长度
    text_count = len(html_urls)
    # 当前爬取章节数
    current = 0
    # 单独处理每个章节的链接
    for content_url in html_urls:
        time.sleep(random.randint(5, 10))
        # 章节链接
        chapter_url = content_url[0]
        # 章节名字
        chapter_name = content_url[1]
        # 获取章节链接源码,转换utf-8格式
        chapter_html = urllib.request.urlopen(text_url + chapter_url).read()
        # urlopen读取的链接格式不同,所以分两种方式转码
        if chr(chapter_html[1]) == '!':
            chapter_html = chapter_html.decode('utf-8')
        else:
            chapter_bytes = BytesIO(chapter_html)
            chapter_html = gzip.GzipFile(fileobj=chapter_bytes).read().decode('utf-8')
        # re匹配正文格式
        chapter_reg = r'&nbsp;&nbsp;&nbsp;&nbsp;(.*?)<br/><br/>'
        chapter_reg = re.compile(chapter_reg, re.S)
        # 获取正文的集合
        context_list = re.findall(chapter_reg, chapter_html)
        f = open('{}.txt'.format(title[0]), 'a', encoding='utf-8')
        # 写入章节名称
        f.write(chapter_name + '\r\n')
        # 将获取的章节正文写入TXT文本
        for context in context_list:
            f.write(context + '\r\n')
        f.close()
        # 用来判断进度
        current += 1
        print(str(current) + '/' + str(text_count))
    print("爬取完成")


getNovelList_biquge()


问题总结

Q1:在使用urlopen访问链接并读取页面的源代码时,可能会出现【读取到不同的编码格式】的情况,如出现【全部为16进制字符】或【HTML源代码夹杂16进制字符】的情况。

A1:
    【全部为16进制字符】的转码
chapter_bytes = BytesIO(chapter_html)
chapter_html = gzip.GzipFile(fileobj=chapter_bytes).read().decode('utf-8')
    【HTML源代码夹杂16进制字符
chapter_html = chapter_html.decode('utf-8')

Q2:在写入本地TXT文本时,可能会因为读取的页面上的一些特殊字符,导致出现【UnicodeEncodeError: 'gbk' codec can't encode character '\u30fb'】的情况。

A2:
    处理方式一(较麻烦):直接去除这些特殊字符(无用),再写入本地文本
t = re.compile('[^A-Z^a-z^0-9^\u4e00-\u9fa5]')
# 将获取的章节正文写入TXT文本
for context in context_list:
    context = t.sub('', context)
    f.write(context + '\r\n')
f.close()
    *注意点:此处直接去除了所有的字符,只保留了英文、中文、数字。 

    处理方式二(推荐):设置写入的编码格式为‘utf-8’,因为Windows默认新文件的编码格式为‘gbk’
f = open('{}.txt'.format('测试文本'), 'a', encoding='utf-8')
    *注意点:如果文件出现乱码,先删除原本的文本,然后重新运行程序。