用Python抓小说

2019-11-22  本文已影响0人  imjcw

前言

有一段时间没有看小说了,前两天看到了唐家三少写的《斗罗大陆II绝世唐门》,突然燃起了看的欲望,就在微信读书上看了起来。

免费章节不多,没多久就看完了。还想继续看,于是,开始花钱了。最终花了¥200+之后,看完了整本书,这时,我看到了第三部...

心想,这也太费钱了。于是想从网上找个资源看看,可看来看去,适合手机端观看的网站且无遮挡广告的太少了,于是想到了用程序抓小说的方式。

功能模块

本次使用的是Python 2.7.6

选择了用Python来抓取小说,毕竟一直听说Python的库非常丰富,自己也没有好好的学过Python,想通过实战来提升一下自己。

大致确定了一下功能,如下:

至此,这样小说就可以抓取完毕了。

选择目标

在目标选择方面,建议选择质量高一点。笔者在这方面没有多做纠结,就找了一个一下子搜到的网站:www.xs.la

列表页分析

目标很简单,只需要获取小说的章节和对应的链接就好,目标网站的列表页结构比较简单,可以很简单就分析好。

红色框:这里有id="list",我们可以用ID的唯一来找到列表内容。

绿色框:有链接,有章节,样式为空

蓝色框:可以不关注的内容,但是样式不为空

小说内容页分析

小说内容也就有些麻烦了,虽然相对于列表页而言,结构简单很多。

同样可以根据ID的唯一找到内容块。

但是,内容里有不少东西是需要去掉的,比如&nbsp;<br/>、等,当然还有些内容可以替换。当然,还有不少内容可以替换,那个时候,对于内容的分析就更加严谨了。

抓取网页很简单--urllib

PHP中,有几个方法可以获取网页内容,比如file_get_contents()fopen()curl等。而Python中我就不知道了,在一番搜索之后,终于让我找到了解决方案。

直接上代码:

# -*- coding: utf-8 -*-
import urllib

url = 'http://www.xs.la/1_1537'
html = urllib.urlopen(url).read()
print(html)

这样,直接将网页的内容抓取了下来。

不过有时候会出现抓取失败的情况,这个时候,需要我们让它有更好的兼容性,不然后续操作会出错的。

# -*- coding: utf-8 -*-
import urllib

url = 'http://www.xs.la/1_1537'
html = urllib.urlopen(url).read()
if not html:
    print('未抓取到网页')
    exit(0)
print(html)

网页抓取完成,那么怎么分析它呢?

正则?貌似会有点麻烦呢,如果能够像jQuery那样,操作htmldom就好了。

项目地址:github

附上urllib的文档:urllib

强大的BeautifulSoup

如果找不到这个库的话,我真的会默默的写正则去,幸好让我遇到了它。

献上BeautifulSoup的文档:BeautifulSoup

献上代码:

# -*- coding: utf-8 -*-
import urllib
from bs4 import BeautifulSoup
...
dom = BeautifulSoup(html)
if not dom:
    print('未获取到dom')
    exit(0)
print(dom)

直接获取到了htmldom,同时根据文档,我们可以使用select方法获取到小说章节列表。

# -*- coding: utf-8 -*-
import urllib
from bs4 import BeautifulSoup
...
dom = BeautifulSoup(html)
if not dom:
    print('未获取到dom')
    exit(0)
storyTagList = dom.select('div#list dd')
print(storyTagList)

继续,我们只是获取到了N个标签,还没有获取到里面的想要的内容。

献上完整代码,同时,添加了备注:

# -*- coding: utf-8 -*-
import urllib
from bs4 import BeautifulSoup

''' 获取网页 '''
url = 'http://www.xs.la/1_1537'
html = urllib.urlopen(url).read()
if not html:
    print('未抓取到网页')
    exit(0)

''' 获取dom '''
dom = BeautifulSoup(html)
if not dom:
    print('未获取到dom')
    exit(0)

''' 获取小说列表dom '''
storyTagList = dom.select('div#list dd')
if not len(storyTagList):
    print('列表为空')
    exit(0)

''' 获取小说列表 '''
storyList = []
for tag in storyTagList:
    ''' 获取a标签 '''
    aTag = tag.select('a')
    if not len(aTag):
        continue
    ''' 判断style是否为空,若不为空,则跳过  '''
    if aTag[0].get('style'):
        continue
    storyInfo = {}
    storyInfo['name'] = aTag[0].string.encode("utf8")
    storyInfo['href'] = aTag[0]["href"]
    storyList.append(storyInfo)
if not len(storyList):
    print('格式化列表失败')
    exit(0)
print(storyList)

项目地址:github

内容分析、获取

遍历很简单,直接上代码了(这里在抓取到第一篇之后,直接退出了):

# -*- coding: utf-8 -*-
import re
import urllib
from bs4 import BeautifulSoup
...
'''遍历列表,获取章节内容'''
compeleteList = {}
for index, chapterInfo in enumerate(storyList):
    try:
        articleUrl = "%s%s" % ('http://www.xs.la', chapterInfo['href'])
        print('获取章节:%s' % chapterInfo['name'])
        html = urllib.urlopen(articleUrl).read()
        if not html:
            raise Exception('未获取到%s的内容' % (chapterInfo['name']))
        print('获取章节dom')
        dom = BeautifulSoup(html)
        if not dom:
            raise Exception('未获取到dom')
            pass
        '''获取章节内容dom'''
        print('获取章节内容dom')
        contentTags = dom.select('div#content')
        if not len(contentTags):
            raise Exception('内容为空')
            pass
        '''解析章节内容'''
        print('解析章节内容')
        contentTagsStr = str(contentTags[0])
        contentFormat = re.sub(r'<br.*?/?>|</?div.*>|<script.*t>|\&.*?;| | ', "\n", contentTagsStr)
        contentList = contentFormat.split("\n")
        contentStr = ''
        '''拼接章节内容'''
        print('拼接章节内容')
        for paragraph in contentList:
            paragraph = str(paragraph)
            paragraph = paragraph.strip()
            if len(paragraph) == 0:
                continue
                pass
            contentStr += '<p>';
            contentStr += str(paragraph);
            contentStr += '</p>';
            contentStr = contentStr.replace('"', "'")
            contentStr = contentStr.replace("'", "'")
        print(contentStr)
        exit(0)
    except Exception as e:
        print(e)

其实这里我做了不少转化,将其拼接成了<p>*</p>格式的,这样我们放在任何一个地方,也可以更好的去控制其展示的样子。

在抓取过程中,我遇到了两个问题。

问题一:抓取网页超时的问题,这个困扰了我一段时间,最终通过度娘解决了:

import socket
socket.setdefaulttimeout(60)

或者转战urllib2

import urllib2
urllib2.urlopen(url, data=None, timeout=60)

问题二:抓取的小说里,有时候有些章节是重复了,怎么办?直接建了一个完成的字典。

...
compeleteList = {}
for index, chapterInfo in enumerate(storyList):
    chapterName = chapterInfo['name']
    if compeleteList.has_key(chapterName):
        continue
        pass
    else:
        compeleteList[chapterName] = chapterName;
        pass
...

最后,我们将内容直接写入文件,小说就抓取完毕。

项目地址:github

前台展示地址:读书

愿景

到这里,小说抓取完毕,在代码层,没有任何设计感而言,后期准备将其功能模块化。

同时,加入重试机制、过滤广告、建立任务系统,通过配置,可以直接抓取指定网站的小说,并时时在前台展示。

结语

在编写脚本的过程中有好几次想要放弃,主要还是对于Python不熟,如果是用PHP写,我可能会写的更快更好吧。

花了一天多的时间,从对于Python的不了解,到写出一个还算能用的小功能,内心的成就感还是蛮不错的。

-- EOF --
本文转载自IMJCW
原文链接:用Python抓小说

上一篇 下一篇

猜你喜欢

热点阅读