Python爬虫实战:爬取全站小说排行榜

喜欢看小说的骚年们都知道,总是有一些小说让人耳目一新,不管是仙侠还是玄幻,前面更了几十章就成功圈了一大波粉丝,成功攀上飙升榜,热门榜等各种榜,扔几个栗子出来:

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这是一个我的python交流群:984632579,不管你是小白还是大牛欢迎入驻 ,讨论技术, 大家一起交流学习成长!

新笔趣阁是广大书友最值得收藏的网络小说阅读网,网站收录了当前......我就不打广告了(其他满足下文条件的网站也行,之前已经有做过简单爬取章节的先例了,但效果不太理想,有很多不需要的成分被留下了,来链接:http://www.bxquge.com/。我们本文就爬取这个网站的上千本小说。重点在和大家一起分享一些爬虫的思路和一些很常遇到的坑。

本文的行文脉络:

1、先构造一个单本的小爬虫练练手;

2、简要分享一下安装MongoBD数据库时的几个易错问题;

3、运用Scrapy框架爬取新笔趣阁全站排行榜。

全部代码可以在公众号后台回复“小说”获取。

一、爬取单本小说

爬取该网站相对来讲还是很容易的,打开编辑器(推荐使用PyCharm,功能强大),首先引入模块urllib.request(Python2.x的引入urllib和urllib2即可,待会我把2.x的也写出来给大家看看),给出网站URL,写下请求,再添加请求头(虽然这个网站不封号,但作者建议还是要养成每次都写请求头的习惯,万一那天碰到像豆瓣似的网站,一不留神就把你封了)话不多说,直接上代码:

#coding:utf-8import urllib.requesturl = "http://www.bxquge.com/3_3619/10826.html"headers = {'User-Agent':            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \            (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063'}request = urllib.request.Request(url,headers=headers)

然后再将请求发送出去,定义变量response,用read()方法观察,注意将符号解码成utf-8的形式,省的乱码:

#coding:utf-8import urllib.requesturl ="http://www.bxquge.com/3_3619/10826.html"headers = {'User-Agent':            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \            (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063'}request = urllib.request.Request(url,headers=headers)response = urllib.request.urlopen(request)html = response.read()html = html.decode("utf-8")print(html)

打印一下看结果:

看到这么一大条就对喽,对比一下网页源码,发现是一致的。

这步观察很重要,因为这就说明该网站没有使用AJAX异步加载,否则就要开始抓包的工作了,这个我们留着分析动态网站时候再说。建议大家在没有更好的方法时使用。之前记得确实有直接判断的方法,然而一不小心忘记了,有知道的读者还请发给我哦。

我们现在得到了网站的response,接下来就是对我们想要获取的数据进行解析、提取,但等等,考虑到我们要爬取大量小说,不搞一个数据库存储真是太失败了,作者推荐MongoDB数据库,属于NOSQL类型数据库,以文档存储为主,这里用来爬小说真是太适合不过了。但安装起来需要一定的程序,想要试着做做的骚年可以参考一下下载和安装教程,参考链接:http://blog.csdn.net/u011262253/article/details/74858211,在安装好后为方便启动,可以添加环境变量,但这里有个坑,你要先打开mongod(注意是mongodb,别一上来就打开mongo),然后需要准确添加dbpath路径,不然打开很容易就会失败,上图上图:

失败状态

成功状态

添加路径后成功连接,出现waiting for connections on port 27017,则表示数据库连接成功,而后就不要关掉这个终端了,只有保持数据库是连接的,才可运行MongoDB数据库(不然报错你都不知道自己是怎么死的)

好了,连接好数据库后,我们将数据库与编辑器进行交互链接,位置很隐秘,在File>>Settings>>Plugins下添加组件Mongo Plugin,没有就下载一个:

盗个图

我们在编辑器内编写代码,引入Python专门用来与MongoDB交互的模块pymongo,然后在最上面链接MongoDB数据库的端口,默认是27017,我们先创建一个叫做reading的数据库,然后在reading内新建一个叫做sheet_words的数据表,代码如下:

#coding:utf-8import pymongoimport urllib.requestclient = pymongo.MongoClient('localhost',27017)reading = client['reading']sheet_words = reading['sheet_words']

我们先找一个叫做《修罗武神》的小说来练练手,个人来讲,我很讨厌看小说时来回的翻页,有时候还跳出广告,这时候我还得返回去重新翻页,作为一名懒得不行的懒人,我想到要是把整部小说放进一个文档里再看不就好了么,但要是一章一章的复制粘贴我想还是算了吧,这时候你就知道爬虫是有多么便捷了。好,现在我们要做的是把《修罗武神》这部小说完整的爬取下来,并在数据库中备份。我们回到刚才停留的地方,在得到response后,我们应该选用一种方法来解析网页,一般的方法有re,xpath,selector(css),建议新手使用xpath而不是re,一是因为re用不好很容易导致错误,“当你决定用正则表达式解决问题时,你有了两个问题。”,相比较xpath才是步骤明确,十分稳妥;二是在Firefox,Chrome等浏览器中可以直接复制xpath路径,大大的减少了我们的工作量,上图:

如果你决定使用xpath之后,我们需要从lxml中引入etree模块,然后就可以用etree中的HTML()方法来解析网页了,从网页>检察元素(F12)中复制下来我们所需数据的路径,我选择的是小说每章的标题和内容,上图,上图:

路径//div[@class="readAreaBox content"]/h1/text()

路径/html/body/div[4]/div[2]/div[2]/div[1]/div[2]/text()

注意注意,又来一个坑,当你复制xpath时得到的是这个东东:

//div[@class="readAreaBox content"]/h1

和这个东东;

/html/body/div[4]/div[2]/div[2]/div[1]/div[2]

但你需要的是这个路径里的文本text,故我们需要另外添加具体文本:/text(),然后就像上面那样啦。上代码,查数据:

url ='http://www.17k.com/list/493239.html'response = urllib.request.urlopen(url)html = response.read().decode("utf-8")tree = etree.HTML(html)dom = tree.xpath('//a[@target="_blank"][@title]/@href')foriindom:data = {'words':"http://www.17k.com"+ i    }    sheet_words.insert_one(data)# $lt/$lte/$gt/$gte/$ne,依次等价于/>=/!=。(l表示less g表示greater e表示equal n表示not  )try:    os.mkdir("修罗武神小说")except FileExistsError:    passos.chdir("修罗武神小说")for item in sheet_words.find():    filename = "修罗武神"    with open( filename,"a+") as f:        contents = urllib.request.urlopen(item["words"])        responses = contents.read().decode("utf-8")        trees = etree.HTML(responses)        title = trees.xpath('//div[@class="readAreaBox content"]/h1/text()')        word = trees.xpath("/html/body/div[4]/div[2]/div[2]/div[1]/div[2]/text()")        a = ''.join(title)        b = ''.join(word)        f.write(a)        f.write(b)        #print(''.join(title))

小说有点大,一共是三千五百章,等个大约4-7分钟吧,打开文件夹《修罗武神小说》,就可以看到我们下载好的无需翻页的一整部小说,数据库内页备份好了每章的链接,它自动从零开始排的,就是说你要看第30章就得打开序号为29的链接,这个调一下下载时的顺序就好了,作者很懒,想要尝试下的读者可以自行更改。

小说文本

数据库连接

看看,感觉还不错吧,好的小例子讲完了,接下来我们准备进入正题。

我们要像上面的例子那样爬取整个网站,当然这里就不再建议使用普通的编辑器来来执行了,聪明的读者已经发现,一部小说爬了4分钟,那么上千本不说,单单是一组排行榜里的100本就够爬好一会了,这就显示出Scripy框架的作用,用专门的Scripy框架写工程类爬虫绝对快速省力,是居家写虫的必备良药哇。

二、爬取小说榜所有小说

首先安装Scrapy的所有组件,建议除pywin32以外都用pip安装,不会的话度娘吧,很简单的,pywin32需要下载与你所用Python版本相同的安装文件。

来连接:https://sourceforge.net/projects/pywin32/

Scrapy插件安装成功

然后还是老规矩,不想每次终端运行都一点一点找路径的话,就将根目录添加到环境变量,然后打开终端,我们测试一下是否安装成功:

Scrapy安装成功

好,安装完毕后,打开终端,新建一个Scrapy工程,这里你可以根据索引,选择使用Scrapy的各种功能,这里不一一详解了,D盘内已经出现了我们建立好的Scrapy工程文件夹:

打开文件夹,我们会看到Scrapy框架已经自动在reading文件夹中放置了我们所需的一切原材料:

打开内部reading文件夹,就可以在spiders文件夹中添加爬虫py代码文件了:

我们这里定向爬小说排行榜,除了我们写的spider文件,还要在items.py中定义我们要爬取的内容集,有点像词典一样,名字可以随便取,但已有的继承类scrapy.Item可不能改,这是Scrapy内部自定义的类,改了它可找不到,spider就用我们上面抓取单本再加一个for循环就OK了,十分简单,一言不合就上图:

# -*- coding: utf-8 -*-import scrapyimport urllib2from lxml import etreeimport osfrom reading.items import ReadingSpiderItemimport sysreload(sys)sys.setdefaultencoding('utf-8')class ReadingspiderSpider(scrapy.Spider):    name = 'bigreadingSpider'#爬虫名    allowed_domains = ['http://www.xinbiquge.com/']#总域    start_urls = ['http://www.bxquge.com/paihangbang/']#起始页    def parse(self, response):        sel = scrapy.selector.Selector(response)#解析域        for i in range(2,10):            path = '//*[@id="main"]/table[' +str(i)+ ']/tbody/tr[1]/td[2]/span'#排行榜路径            sites = sel.xpath(path)            items = []            for site in sites:#按9个排行榜爬取                item = ReadingSpiderItem()                path_list_name = '//*[@id="main"]/table[' +str(i)+ ']/tbody/tr[1]/td[2]/span/text()'                item['list_name'] = site.xpath(path_list_name).extract()#排行榜名称                try:                    os.mkdir(item['list_name'][0])                except IOError:                    pass                os.chdir(item['list_name'][0])                for li in range(1,21):                    title = '//*[@id="tb' + str(i-1) + '-1"]/ul/li['+str(li)+']/a/text()'                    item['name'] = site.xpath(title).extract()#书名                    url = 'http://www.bxquge.com/'                    path_link = '//*[@id="tb'+ str(i-1) +'-1"]/ul/li[' + str(li) + ']/a/@href'#书目链接                    item['link'] = site.xpath(path_link).extract()                    full_url = url + item['link'][0]                    headers = {'User-Agent':                                        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \                                        (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063'}                        #request = urllib2.Request(''.join(item['link']),headers=headers)                    request = urllib2.Request(full_url,headers=headers)                    try:                          os.mkdir(item['name'][0])                    except IOError:                        pass                    os.chdir(item['name'][0])                    response = urllib2.urlopen(request).read().decode('utf-8')                    tree = etree.HTML(response)                    for j in range(1,11):                            path_title = '//*[@id="list"]/dl/dd['+ str(j) +']/a/text()'                            item['title'] = tree.xpath(path_title)#章节名                            path_words_page = '//*[@id="list"]/dl/dd['+ str(j) +']/a/@href'                            word_page = tree.xpath(path_words_page)                            try:                                url_words = full_url +  word_page[0]                                request_words = urllib2.Request(url_words,headers=headers)                                response_words = urllib2.urlopen(request_words).read().decode('utf-8')                                tree_words = etree.HTML(response_words)                                path_words = '//*[@id="content"]/text()'                                item['words'] = tree_words.xpath(path_words)#前20章内容                            except urllib2.HTTPError:                                pass                            items.append(item)                            try:                                filename = r''.join(item['title'][0]) + '.json'                                with open(filename,"a+") as f:                                    a = ''.join(item['title'])                                    b = ''.join(item['words'])                                    f.write(a)                                    f.write(b)                            except IOError:                                pass                    path_now = os.getcwd()                    path_last = os.path.dirname(path_now)                    os.chdir(path_last)                path_now = os.getcwd()                path_last = os.path.dirname(path_now)                os.chdir(path_last)

Python的确是一个激动人心的功能强大的语言。Python那种性能和特性的恰到好处的组合让使用Python编程既有趣又简单。

好了就不说废话了,现在给你们分享我的Python学习资料喽!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容