第一次编写爬虫网站。(感觉有点较凌乱)
各位大神,如果有更好的意见,欢迎指点建议。谢谢。
目前代码基本上能按照正常的思路扒取小说内容。
思路如下:首先进入小说网站首页---》然后选取要下载的系列----》然后开始下载当前系列的所有书本链接,并存放json----》然后读取json文件内的数据进行下载。
目前代码能够按照思路进行下载,但是下载速度太慢了 ,没有进行多线程下载(持续更新这部分功能)。我反爬机制没做好,如果在同一个局域网,两台不同的电脑&不同的请求头,同时运行,会处罚到网站的反爬机制,导致页面返回出错。还需要优化。
在头文件处,添加浏览器本身的cookie信息,可以解决出错这个问题。
以下是效果图。
源码在文末。
#这是链接文件。
# -*- encoding=utf8 -*-
#zj为章节的意思
from bs4 import BeautifulSoup
import requests
import json #存储书本链接文件
import pandas as pd #清洗数据,去掉重复的链接需要用到
import os #检查当前目录下是否有对应的文件需要用到
#导入这个urllib3 这个库是为了屏蔽报错信息
from requests.packages.urllib3.exceptions import InsecureRequestWarning
html_base = 'http://www.qu.la/'
HEADERS= {
'User-Agent':'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
'Referer': 'https://www.qu.la/book/68/',
'Host': 'www.qu.la',
'Cookie': '【这里添加自己的cookie信息】'}
xl = {}
xl_link = {}
zj_list_links = [] #收集书本的所有链接
zj_list_html_dict = {} # total(int):小说总数 next_href:下一页的链接
storyinfo = {} # 收集小说的书名、作者、简介、等信息
over_mark = True
over_mark1 = True
over_mark_getlinks = True
#下载正文 ,下载完成后打开下一章继续下载,直至到完成一本书的下载
def get_story(soup):
#下载第几章
storyinfo["第N章"] = soup.select(".reader-main .title")[0].text
storyinfo["正文"] = soup.select(".container .content")[0].text # 如果不加.text 就回出现<br/>符号。加上后就回只留下文本。
storyinfo["下一页link"] = soup.select(".section-opt a")[2]['href']
#每本书的基本信息zjnum = -20
def story_info(soup):
#小说名称
storyinfo["书名"] = soup.select(".top h1")[0].text
# 小说系列
storyinfo["系列"] = soup.select(".xs-show")[0].text
# 小说更新日期
storyinfo["更新时间"] = soup.select(".fix p")[4].text
# 小说作者l
storyinfo["作者"] = soup.select(".fix p")[0].text
#小说简介
storyinfo["简介"] = soup.select(".info .desc.xs-hidden")[0].text
#书本第一章
storyinfo["第一章link"] = soup.select(".container .section-box li a")[-20]['href']
#收集小说总数,以及20本之后的下一页链接
def zj_list_html(soup):
#当前系列中所有小说的总数
total_story = soup.select(".pagination.pagination-mga .hd strong")[0].text
#"最近更新小说页面的#下一页#"
zj_list_nextpage = soup.select(".pagination.pagination-mga li a")[0]['href']
zj_list_html_dict["书本总数"] = int(total_story)
zj_list_html_dict["下一页link"] = zj_list_nextpage
for i in range(20):
zj_links = soup.select(".layout.layout2.layout-col2 .txt-list.txt-list-row5 li .s2 a")[i]['href']
zj_list_links.append(zj_links)
#打开网页
def open_html(href):
#randomnum = random.randrange(1, 35)
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
res = requests.get(html_base + href,verify= False,headers=HEADERS)
soup = BeautifulSoup(res.text, "lxml")
return soup
#新建txt,并追加内容。可在这个方法里进行数据清洗,和小说排版等工作
def download_newfile(file_text,filename,zjname):
#创建文件夹
zj_file = open(filename+'.txt',"ab+") #这里 ab+ 标记出了使用二进制打开txt
# 数据清洗
newfile = str(file_text)
newfile = newfile.replace("章节错误,点此举报(免注册),举报后维护人员会在两分钟内校正章节内容,请耐心等待,并刷新页面。",'\n')#文本格式整理#文本格式整理
newfile_encode = newfile.encode("utf-8") #这里必须转换成UTF-8再写入txt 否则回报错
zjname_encode = str(zjname).encode("utf-8")
#数据写入
zj_file.write(zjname_encode)
zj_file.write(newfile_encode)
zj_file.close()
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
res = requests.get(html_base,verify = False)
res.encoding = "utf-8"
soup = BeautifulSoup(res.text,"lxml")
#收集系列名称并写入dict(xl)
for i in range(1,7):
xl[i] = soup.select(".nav li a")[i+1].text
#收集系列名称并写入dict(xl_link)
for i in range(1,7):
xl_link[i] = soup.select(".nav li a")[i+1]['href']
# 显示选项,并选择需要下载的系列
print(xl)
num = input("Please input your choose:")
if __name__ == '__main__':
if bool(num) == True:
# 打开这个链接可以获取当前页面20本书的链接和下一页的链接
zj_list_html(open_html(xl_link[int(num)]))
# 检索目前路径数据库
if os.path.exists('xl_all_links' + '_' + xl[int(num)] + '.json') == True :
print("书本链接文件已存在,正在读取链接文件...")
with open('xl_all_links' + '_' + xl[int(num)] + '.json','r') as f:
zj_list_links = json.load(f)
print(zj_list_links)
alllinks = len(zj_list_links)
print("书本总数:"+str(alllinks))
while over_mark:
for i in range(alllinks):
print("当前下载:第"+str(i+1)+"本")
# 从story_info获取第一章的链接并打开
story_info(open_html(zj_list_links[i]))
print(storyinfo["书名"])
over_mark1 = True
while over_mark1:
get_story(open_html(storyinfo["第一章link"]))
while zj_list_links[i] != storyinfo["下一页link"]:
download_newfile(storyinfo["正文"], storyinfo["书名"], storyinfo["第N章"])
try:
get_story(open_html(storyinfo["下一页link"]))
#当收集到最后一页的时候,返回就会出错,首先返回出错网页,再返回书本列表。这里检测到这个信息后,就表示已收集完成了。
#现在在头文件增加了cookie 后,这里已经大幅度减少报错了。
if storyinfo["下一页link"] == "javascript:void(0);":
if 0==alllinks-1:
print("当前书籍收集完成。")
break
storyinfo["下一页link"] = zj_list_links[i]
except:
print("这里出错了,出错内容:")
print(storyinfo["第N章"])
print("出错链接:"+storyinfo["下一页link"])
print("正重新尝试")
continue
over_mark1 = False
#print("当前章节链接:"+storyinfo["下一页link"]) #打印出当前章节链接
print("正在下载:"+ storyinfo["第N章"]) # 当前下载的章节
continue
alllinks = alllinks-1 #下载完成后,总链接 -1
else:
print("当前目录未发现链接文件,请先下载书本链接...")
# 选中系列打开网址
print("正在下载" + xl[int(num)] + "书本链接...")
print("当前系列书本总数:"+str(zj_list_html_dict["书本总数"]))
# 这里开始收集书本中的所有链接
# 并开始清洗收集到的数据#使用pandas unique 进行清除重复值
# 最终保存数据文件为json
while over_mark_getlinks:
getlinks = open_html(zj_list_html_dict["下一页link"])
try:
zj_list_html_dict["下一页link"] = getlinks.select(".pagination.pagination-mga li a")[1]['href']
for i in range(20):
zj_links = getlinks.select(".layout.layout2.layout-col2 .txt-list.txt-list-row5 li .s2 a")[i]['href']
zj_list_links.append(zj_links)
print("已下载链接:"+str(len(zj_list_links)))
except:
print("页面异常!停止收集,已收集:")
print(len(zj_list_links))
print(zj_list_html_dict["下一页link"])
print("收集完毕,正在清洗整理文件并保存....")
zj_list_links = pd.unique(zj_list_links)
print(len(zj_list_links))
with open('xl_all_links' + '_' + xl[int(num)] + '.json', 'w') as file:
json.dump(list(zj_list_links), file)
print("存储文件:" + 'xl_all_links' + '_' + xl[int(num)] + "-->保存完毕!")
print("下载完毕请重新运行软件。")
break