学爬虫,拿平常看小说的绿色网站下手。
爬取的数据主要分为两部分,收藏榜的小说信息和小说详情页的部分数据。
通过点击榜单上侧选项(其实也可以用拼音猜一猜),观察url变化,寻找规律。如fw指代范围,fbsj指代发表时间,ycx指代原创性,以此类推。可以通过改变其后的数字,来改变榜单范围。而最重要的翻页就通过改变page=后的页码。
我没什么要先定的范围,就只更改page。
随意点击任意小说详情页,可以看到主要区别就在于最后novelid的一串数字,看起来没有什么规律的数字。
但是通过后面的页面分析,可以看到小说详情页的url其实就包含在html中,只要记录下之后再转接即可。
鼠标右键查看审查元素,观察。
可以看到,整个收藏榜信息均在一个table之内,一行是一个tr,每格是一个td。
木苏里 全球高考 原创-纯爱-近代现代-剧情 轻松 完结 589514 35908325376 2018-10-10 20:03:00
以第一部作品为例,按顺序,可以爬取到作者(木苏里)、作品详情页链接(oneauthor.php?authorid=966799)、简介+标签(简介:并肩炸考场
标签:强强 无限流 相爱相杀 未来架空)、作品(全球高考)、类型(原创-纯爱-近代现代-剧情)、风格(轻松)、进度(完结)、字数(589514)、作品积分(35908325376)、发表时间(2018/10/10 20:03:00)。
整个表格都比较有规律,先findall tr,再根据html写出对应的正则compile。
详情页主要准备爬取三个地方:文案、文章基本信息、章节表格结尾数据
1)文案
2)作品视角、是否出版、签约状态
3)总书评数、当前被收藏数、营养液数
同样是查看审查元素,写出正则表达式,但在这里遇到了不少问题:
1)审查元素与源代码有差别
部分数据,如非v章节章均点击数,审查元素有,但是源代码里是空白:
这是个动态元素,后来找到了解决办法,但是要再次跳转页码过于麻烦,所以决定放弃这个数据。
2)区域html差别大
爬取的三个地方,不像收藏榜是在一个table里,很难findall先锁定一个大区域,尝试之后,东拼西凑,文案和书评收藏数用compile,文章基本信息里,先findall了ul。
3)是否出版扒不下来
原本想把title里的文字给扒下来,但是无论如何compile都是空白,最后草率地写了个if else简单判断,手动append上是否出版的信息。
晋江不登录的话,只让爬取前十页的数据,十页以后会有登录提示,爬虫也自动断了,上网找到了解决方法,登录晋江账号后,查看审查元素,并刷新页面,找到如下所示:
将cookie复制,加到爬虫的请求头中。
import requests from bs4 import BeautifulSoup import time import re import xlwt t1 = time.time() def main(savepath): count=0 for page in range(1,501): url = get_url(page) headers = { 'cookie': '你的cookie'} html = requests.get(url, headers=headers) html.encoding = html.apparent_encoding try: datalist = getData(html.content) except: print("爬取失败:", page) continue if len(datalist) == 0: break saveData(savepath,datalist,count) count+=len(datalist) print(page) print(count) #time.sleep(3) def get_url(page): url = f"https://www.jjwxc.net/bookbase.php?fw0=0&fbsj0=0&ycx0=0&xx0=0&mainview0=0&sd0=0&lx0=0&fg0=0&bq=-1&sortType=4&isfinish=0&collectiontypes=ors&page={page}" return url #提取收藏榜数据的正则表达式 findAuthor = re.compile(r'">(.*?)',re.U) findName = re.compile(r'">(.*?)',re.U) findBookLink = re.compile(r'", re.I|re.S|re.M) findType = re.compile(r'(.*?) ', re.S|re.M) findStyle = re.compile(r'(.*?) ', re.S|re.M) findPro = re.compile(r'(.*?) ', re.S|re.M) findSize = re.compile(r'(\d+) ') findPoint = re.compile(r'(\d+) ') findTime = re.compile(r'(.*?) ') def getData(document): datalist = [] soup = BeautifulSoup(document, "html.parser") i = 0 for item in soup.findAll('tr'): data = [] # 保存一本书的所有信息 item = str(item) Author = re.findall(findAuthor, item)# 提取作者名 Name = re.findall(findName, item)# 提取文名 BookLink = re.findall(findBookLink, item)# 提取文章链接 Title = re.findall(findTitle, item)# 提取标签 Type = re.findall(findType, item)# 提取类型 Style = re.findall(findStyle, item)# 提取风格 Pro = re.findall(findPro, item)# 提取进度 Size = re.findall(findSize, item)# 提取字数 Point = re.findall(findPoint, item)# 提取积分 Time = re.findall(findTime, item)# 提取发表时间 if i: data.append(Author[0]) data.append(Name[1]) BookLink[1]='http://www.jjwxc.net/' + BookLink[1] data.append(BookLink[1]) Title[1]=re.findall(r'title="(.*?)"', Title[1], re.S|re.M) data.append(Title[1]) data.append(Type[0].strip()) data.append(Style[1].strip()) if '' in Pro[2]: Pro[2]=re.findall(r'>(.*?)', Pro[2]) data.append(Pro[2]) else: data.append(Pro[2].strip()) data.append(int(Size[0])) data.append(int(Point[1])) data.append(Time[0]) #进入小说详情页,爬取相关数据 try: detail(BookLink[1],data) except: print("爬取失败:", BookLink[1]) continue datalist.append(data) i = 1 return datalist def getURL(url): headers = { 'User-Agent': 'Mozilla/5.0 (MSIE 10.0; Windows NT 6.1; Trident/5.0)' } response = requests.get(url,headers = headers) response.encoding = 'gbk' time.sleep(0.2) if response.status_code == 200: return response.text return None def detail(url,data): html = getURL(url) soup = BeautifulSoup(html, "html.parser") #爬取书评、收藏、营养液数 comment = re.compile('.*? .*?.*?(.*?).*?(.*?).*?(.*?).*?(.*?).*?',re.S) items = re.findall(comment,html) for j in items: data.append(j[1])#书评数 data.append(j[2])#当前被收藏数 data.append(j[3])#营养液数 #爬取文案 copywriting = re.compile(' .*?(.*?)',re.S) items = re.findall(copywriting,html) #value = items[0].replace('
', '').replace(' ', '') pattern = re.compile(r'<[^>]+>',re.S) value = pattern.sub('', items[0]) data.append(value) # 爬取作品视角、出版、签约状态 result_list = soup.find_all('ul',attrs={'name':'printright'}) for results in result_list: result=str(results) infor = re.compile('(.*?)',re.S|re.M) items = re.findall(infor,result) data.append(items[2].strip())#作品视角 if 'img' in items[7]:#是否出版 data.append("已出版") else: data.append("尚未出版") items[9]=re.findall(r'>(.*?)', items[9])#是否签约 data.append(items[9][0]) workbook = xlwt.Workbook(encoding='utf-8',style_compression=0) worksheet = workbook.add_sheet('晋江', cell_overwrite_ok=True) col = ("作者","作品","链接","标签","类型","风格","进度","字数","作品积分","发表时间","总书评数","当前被收藏数","营养液数","文案","视角","出版状态","签约状态") for i in range(0,17): worksheet.write(0,i,col[i]) # 3.保存数据 # 保存到文件中 def saveData(savepath,datalist,count): for i in range(0,len(datalist)): data = datalist[i] for j in range(0,len(data)): worksheet.write(count+i+1,j,data[j]) workbook.save(savepath) if __name__ == "__main__": main("你的存储路径") print("耗时:", time.time() - t1)5结果
500页每页100本小说,最后爬取出来46953条数据。
6总结
回忆性文章,其实过程中遇到过很多问题和困难,但暂时只想起这些了。
待改善的地方:
1)爬取太慢,爬取250页花费近10个小时,看网上有多进程、多线程可以加快爬虫时间,之后有时间当学习改进;
2)正则表达式不够精确,部分详情页爬取出来不是目的数据,因为错误的数据量不多,后续数据处理采取了直接删除的办法,之后可以在爬虫阶段尝试改进;
3)部分收藏榜页整页爬取失败,部分详情页爬取失败,失败原因待查。