这次爬取轻小说是在我昨天刚发现的一个轻小说网站 真白萌Web小镇, 该网站并没有浏览器检测, 所以并不用伪装浏览器, 直接使用 requests.get(url)即可.
分析源代码
我们先以 《那个人后来》 这本轻小说为例:
主页面
我们首先分析它的主页面, 点击右键查看源代码, 这里列取比较重要的部分:
1 2 3 4 5 6 7 8
| ...... <title>那个人后来 - 真白萌Web小镇 - Powered by Discuz!</title> ...... <th class="common"> <a href="javascript:;" id="content_1011" class="showcontent y" title="更多操作" onclick="CONTENT_TID='1011';CONTENT_ID='normalthread_1011';showMenu({'ctrlid':this.id,'menuid':'content_menu'})"></a> <em>[<a href="https://masiro.moe/forum.php?mod=forumdisplay&fid=74&filter=typeid&typeid=190">第一章</a>]</em> <a href="https://masiro.moe/forum.php?mod=viewthread&tid=1011&extra=page%3D1" style="color: #3C9D40;" onclick="atarget(this)" class="s xst">177 积累压力的工作</a> </th> ......
|
我们可以从中看到, 小说的名称储存在 tag ‘title’ 中; 小说每个章节 储存在 tag ‘a’ 的 class=”s xst” 中的 ‘herf’ 中, 但是储存在这这个块里的链接不仅仅是小说章节, 还有很多乱七八糟的链接, 这时就需要我们匹配小说链接独特的 text 了, 小说的 text 都是有规律的, 有 “第xx话”, “第xx章”, “第xx回”, “xx章”, “xx话”, “xx回”等, 我们可以使用正则匹配 text=re.compile(r”^[\d]|[第]”). 接下来要将所有章节的网址储存起来, 以便我们接下来对每个章节内容进行爬取.
这样我们可以写出以下代码
1 2 3 4 5 6 7 8 9
| r = requests.request('GET', URL) soup = BeautifulSoup(r.text, 'html.parser') bookName = soup.title.string infoList = [] r = requests.request('GET', url) soup = BeautifulSoup(r.text, 'html.parser') tag = soup.findAll('a', class_="s xst", text=re.compile(r"[^\d]|[第]")) for j in range(len(tag) - 1, -1, -1): infoList.append(tag[j]['href'])
|
分页
有的小说的章节主页面放不下, 可能会分多页储存. 这时我们先观察各个页码链接的特征, 可以发现小说主页面第一页的 url 为 https://masiro.moe/forum.php?mod=forumdisplay&fid=74&page=1
, 第二页的 url 为 https://masiro.moe/forum.php?mod=forumdisplay&fid=74&page=2
, 只改变了 page 的值, 再看主页面的源代码, <span title="共 4 页">
小说的总页数是储存在 tag ‘span’ 的 ‘title’ 中, 于是我们就可以写出以下代码:
1 2
| pageTag = soup.find('span', title=re.compile(r"^共")) totalPage = int(page['title'].split(' ')[1])
|
然后我们再根据总页数, 即可找到各个页面的网页.
到现在为止, 我们可以写出以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| r = requests.request('GET', URL) soup = BeautifulSoup(r.text, 'html.parser') infoList = [] totalPage = 1 bookName = soup.title.string pageTag = soup.find('span', title=re.compile(r"^共")) if(pageTag): totalPage = int(pageTag['title'].split(' ')[1]) for i in range(totalPage, 0, -1): url = URL + "&page={}".format(i) r = requests.request('GET', url) soup = BeautifulSoup(r.text, 'html.parser') tag = soup.findAll('a', class_="s xst", text=re.compile(r"[^\d]|[第]")) for j in range(len(tag) - 1, -1, -1): infoList.append(tag[j]['href'])
|
文章
我们进入文章界面, 右键打开源代码文件, 寻找文章所在的位置; 可以看到, 文章位于 tag ‘table’ 中的第五个 ‘table’, 网页的文章排版比较乱, 我们先将换行符和空白区域全部删除, 再针对文章必有换行符</ br>, 使用正则匹配 re.compile(r"br/>[^<]+<|>[^<]+<br/")
.
提取文章的完整代码为:
1 2 3 4 5 6 7 8 9 10
| for i in range(len(infoList)): r = requests.request('GET', infoList[i]) soup = BeautifulSoup(r.text, 'html.parser') title = soup.title.string tag = soup.find_all('table')[4] text = tag.prettify() text = text.replace('\n', '') text = text.replace(' ', '') rule = re.compile(r"br/>[^<]+<|>[^<]+<br/") content = re.findall(rule, text)
|
写入文件
我们再将获取的文章写入txt, 需要注意的是, 我们获取的文本并不全是文章, 还包含了<br/>符号, 需要再次匹配获取纯文章.
1 2 3 4 5 6
| f.write('\n\n\n\n' + title + '\n\n') for i in range(len(content)): rule = re.compile(r">[^<]+<") line = rule.search(content[i]).group() f.write(" " + line[1:-1] + "\n") f.flush()
|
完整代码
到这里我们就基本完成了轻小说的爬取, 这里我贴出完整代码, 事实上还有很多地方需要改进, 这里贴出的代码仅供学习:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
import requests import time from bs4 import BeautifulSoup import re
def getInfoList(URL): totalPage = 1 try: r = requests.request('GET', URL) except: print("获取网页失败, 请稍后再试") soup = BeautifulSoup(r.text, 'html.parser') infoList = [] pageTag = soup.find('span', title=re.compile(r"^共")) bookName = soup.title.string if(pageTag): totalPage = int(pageTag['title'].split(' ')[1]) for i in range(totalPage, 0, -1): url = URL + "&page={}".format(i) r = requests.request('GET', url) soup = BeautifulSoup(r.text, 'html.parser') tag = soup.findAll('a', class_="s xst", text=re.compile(r"^[\d]|[第]")) for j in range(len(tag) - 1, -1, -1): infoList.append(tag[j]['href']) return infoList, bookName
def getbook(infoList, bookName): f = open(bookName + ".txt", 'w', encoding='utf-8') f.write(bookName) for i in range(len(infoList)): time.sleep(0.01) try: r = requests.request('GET', infoList[i]) except: print("分析失败了, 稍后再试吧") soup = BeautifulSoup(r.text, 'html.parser') title = soup.title.string tag = soup.find_all('table')[4] text = tag.prettify() text = text.replace('\n', '') text = text.replace(' ', '') rule = re.compile(r"br/>[^<]+<|>[^<]+<br/") content = re.findall(rule, text) __write(content, title, f) f.close() print("下载完成")
def __write(content, title, f): try: f.write('\n\n\n\n' + title + '\n\n') for i in range(len(content)): rule = re.compile(r">([^<]+)<") line = rule.search(content[i]).group(1) f.write(" " + line + "\n") print("完成 " + title) f.flush() except: print("写入章节失败")
if __name__ == "__main__": URL = input(请输入要下载的网址) print("url为: " + URL + "\n开始下载...") infoList, bookName = getInfoList(URL) getbook(infoList, bookName)
|
用Scrapy库写的爬虫代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| import scrapy import re import os from bs4 import BeautifulSoup
class DemoSpider(scrapy.Spider): name = 'demo' start_urls = ['https://masiro.moe/forum.php?mod=forumdisplay&fid=74'] bookName = 'none'
def parse(self, response): soup = BeautifulSoup(response.body, 'html.parser') totalPage = 1 pageTag = soup.find('span', title=re.compile(r"^共")) self.bookName = soup.title.string.split(' ')[0] if not os.path.exists(self.bookName): os.mkdir(self.bookName) if(pageTag): totalPage = int(pageTag['title'].split(' ')[1]) for i in range(totalPage, 0, -1): url = self.start_urls[0] + "&page={}".format(i) yield scrapy.Request(url=url, callback=self.parse_pages)
def parse_pages(self, response): soup = BeautifulSoup(response.body, 'html.parser') tag = soup.findAll('a', class_="s xst", text=re.compile(r"^[\d]|第")) infoList = [] for j in range(len(tag) - 1, -1, -1): infoList.append(tag[j]['href']) for url in infoList: yield scrapy.Request(url=url, callback=self.parse_web)
def parse_web(self, response): soup = BeautifulSoup(response.body, 'html.parser') title = soup.title.string tag = soup.find_all('table')[4] text = tag.prettify() text = text.replace('\n', '') text = text.replace(' ', '') rule = re.compile(r"br/>[^<]+<|>[^<]+<br/") content = re.findall(rule, text) fname = self.bookName + '/' + title.split(' ')[0] + '.txt' with open(fname, 'wb') as f: f.write(('\n\n\n' + title + '\n\n').encode('utf-8')) for i in range(len(content)): rule = re.compile(r">[^<]+<") line = rule.search(content[i]).group() f.write((" " + line[1:-1] + "\n").encode('utf-8')) self.log('成功写入: %s' % title)
|