起点小说的爬虫是我写的第一个程序,但是之前的那个写的是真的太垃圾了,爬下来的东西也不是人能看的,所以就趁着自己有时间,重新写了一个,稍微优化了一下下
按流程来吧,首先先导入所需要的库
from urllib.request import urlopen
from bs4 import BeautifulSoup
import time
打开起点中文网中,免费专区
https://www.qidian.com/free
上一个版本中,我写的爬虫是需要用户来给定URL的,但是想了想这一点都不爬虫,于是我就把爬虫获取书籍url的部分写上去了
首先先看看小说名和URL在哪个元素下
发现在div下的h4下的a元素,是链接地址和小说名。
Python中利用Bs4查找的方法有很多种,怎么用看个人喜好,这里给个url供参考就好了
https://www.cnblogs.com/gl1573/p/9480022.html
如果不是特别奇怪的那种,我一般都是一级一级的方式来查找的
.....
url="https://www.qidian.com/free" #免费区的url
html=urlopen(url)
bsObj=BeautifulSoup(html,"html5lib") #分析源码
fname=bsObj.select('div > h4 > a') #查找div下的h4下的a
因为查找出来的结果不只有一个,所以得通过循环来将内容输出出来
.....
fname=bsObj.select('div > h4 > a') #查找div下的h4下的a
for i in fname:
a=i.get_text()
b=i.get('href')
print(a+b)
有了名字和url后,当然不能只是print这么简单,我这里采用的方法是,添加进列表里
先在上面创建两个列表,一个是存名字的,一个是存url的
book_name=[]
book_url=[]
接着,用进for循环中.
.....
#改动后
book_name=[]
book_url=[]
num=1
for i in fname:
if num==7:
break
a=i.get_text()
print("["+str(num)+"]: "+a)
b=i.get('href')
book_name.append(a)
book_url.append(b)
num+=1
上面的num,是用来给用户选择所需要爬取的书籍,用户输入ID对应ID就可以选择了,效果图是这样的
有了书的URL后,打开来就到这个页面了,因为这里还不是小说正文部分,所以还需要进一步的获取URL,还是老套路,找到 免费试读
所在的部分
发现是在id叫做 redBtn
的元素下,安排
def get_url(url):
html=urlopen("https:"+url)
bsObj=BeautifulSoup(html,"html5lib")
neir=bsObj.select('#readBtn') #井号可代替id,#readBtn就等于id=readBtn
nurl=neir[0].get('href') #因为是列表的属性,所以得[]
return nurl
这个时候,打开来才是小说的正文部分,这里首先考虑三个点
第一得先知道我爬取的小说名叫什么,这个是后面作为保存的文件名,你可以从这里获取
当然也可以直接从前面获取的 book_name
保存来用
whichbook=int(input("请选择书籍ID:")
bkname=book_name[whichbook-1]
因为给人看的话,从1开始会比较好,但是python中的索引是从0开始,所以在选择的时候,得减去1,这样才是正确的
第一点解决了,现在来看看第二点,小说章节名,章节名可以说比文件名重要,毕竟如果没了章节名,看到第几章都不知道,没有一个分隔的地方了
我这里的写法比较懒,因为在开发者工具中,直接就看到了 title
是章节名,就直接拿来用了(也算标明出处?)
html=urlopen(url) #获取源码
bsObj=BeautifulSoup(html,"html5lib") #分析
bt=bsObj.find('title') #获取章节名
print(bt.get_text()) #获取文本内容
第二点倒是很简单就按排上了,下面就是第三点了,小说正文
我第一次写的时候的效果是这样的
这排版。。老实说。就是屎。。
于是乎,稍微改动了一下,原本是一整个正文当作一个部分来处理,现在拆开来,每一句后面都加一个 \n
,改动后得效果就是这样了
写是这样写的
先找到 divclass="read-content">
bsObj=BeautifulSoup(html,"html5lib")
chapter=bsObj.find("div",{"class","read-content"})
接着在写一个利用 find_all()
的,将里面的p全部获取一遍,然后每一个,后面都加上一个 \n
就好了
...
chapter=chapter.find_all("p")
for i in chapter:
print(i.get_text().replace(" ","")+"\n")
"replace是用来删除空格的,感觉没啥用"
文件名,章节名,正文都有了,接下来就是怎么把“下一章”的内容给爬取出来的问题了
开发者工具中查看位置
然后.find()查找
while True:
html=urlopen(url)
bsObj=BeautifulSoup(html,"html5lib")
bsoup=bsObj.find("",{"id":"j_chapterNext"})
url="http:"+bsoup.get('href')
这样就能一直下一页,直到结束了,为什么url前面要加一个 http:
呢,因为他获取下来的 href
,是没有这玩意的,如果拿这个直接去请求的话,会提示url不存在,所以得拼接一下,除此之外,这里还有个问题,怎么判断小说结束了,毕竟我是无限循环
如果你是最后一章的话,那么就不存在正文和标题这两个玩意了,那么在获取的时候,便是空内容,这时,程序会报错,所以只需要写多一个异常处理就好了,写入文件这部分,整合一下就变成了
url="xxx"
while True:
html=urlopen(url)
try:
bsObj=BeautifulSoup(html,"html5lib")
bt=bsObj.find('title') #查找章节名字
chapter=bsObj.find("div",{"class","read-content"}) #查找小说
chapter=chapter.find_all("p")
fo=open(bkname+".txt","a",encoding='UTF-8') #保存txt文件,并将名字设置为小说名
fo.write("\n"+bt.get_text()+"\n") #内容写入
for i in chapter:
fo.write("\n"+i.get_text().replace(" ","")) #内容写入
fo.close() #关闭文件
bsoup=bsObj.find("",{"id":"j_chapterNext"}) #获取下一章的url
url="http:"+bsoup.get('href')+".html" #拼接语句
except:
print ("抓取完毕.....")
time.sleep(2)
break
如果报错了,就说明是到尾章了,认为抓取完毕,休息两秒,退出循环
到这里整个程序就全部都写完了
源码我放在了 github
,有需要的自行下载就好了
https://github.com/Ernket/qidianpachong/tree/master