爬虫|python爬取某趣阁小说(2.0),十分钟爬完一千六百章
【爬虫|python爬取某趣阁小说(2.0),十分钟爬完一千六百章】
python爬虫高效爬取某趣阁小说
这次的代码是根据我之前的 笔趣阁爬取 的基础上修改的,因为使用的是自己的ip,所以在请求每个章节的时候需要设置sleep(4~5)才不会被封ip,那么在计算保存的时间,每个章节会花费6-7秒,如果爬取一部较长的小说时,时间会特别的长,所以这次我使用了代理ip。这样就可以不需要设置睡眠时间,直接大量访问。
一,获取免费ip
关于免费ip,我选择的是站大爷。因为免费ip的寿命很短,所以尽量要使用实时的ip,这里我专门使用getip.py来获取免费ip,代码会爬取最新的三十个ip,并以字典的形式返回两种,如{’http‘:’ip‘},{’https‘:’ip‘}
文章图片
文章图片
!!!!!!这里是另写了一个py文件,后续正式写爬虫的时候会调用。
import requests
from lxml import etree
from time import sleepdef getip():
base_url = 'https://www.zdaye.com'
url = 'https://www.zdaye.com/dayProxy.html'
headers = {"User-Agent": "Mozilla/5.0 (X11;
Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
}res = requests.get(url, headers=headers)
res.encoding = "utf-8"
dom = etree.HTML(res.text)
sub_urls = dom.xpath('//h3[@class ="thread_title"]/a/@href')sub_pages =[]
for sub_url in sub_urls:
for i in range(1, 11):
sub_page = (base_url + sub_url).rstrip('.html') + '/' + str(i) + '.html'
sub_pages.append(sub_page)
http_list = []
https_list = []
for sub in sub_pages[:3]:
sub_res = requests.get(sub, headers=headers)
sub_res.encoding = 'utf-8'
sub_dom = etree.HTML(sub_res.text)
ips = sub_dom.xpath('//tbody/tr/td[1]/text()')
ports = sub_dom.xpath('//tbody/tr/td[2]/text()')
types = sub_dom.xpath('//tbody/tr/td[4]/text()')
sleep(3)
sub_res.close()for ip,port,type in zip(ips, ports,types):
proxies_http = {
}
proxies_https= {
}
http = 'http://' + ip + ':' + port
https = 'https://' + ip + ':' + port
#分别存储http和https两种
proxies_http['http'] = http
http_list.append(proxies_http)proxies_https['https'] = https
https_list.append(proxies_https)returnhttp_list,https_listif __name__ == '__main__':
http_list,https_list = getip()print(http_list)
print(https_list)
二,完整代码放在最后后面了,这里的 from getip import getip 就是前面获取ip部分。
这里我收集数十个常用的请求头,将它们与三十个IP随机组合,共可以得到300个左右的组合。
这里我定义了三个函数用于实现功能。
biquge_get()函数:输入搜索页面的url,关于搜索的实现是修改url中的kw,在main函数中有体现。
--------------------------返回书籍首页的url和书名。
get_list()函数:输入biquge_get返回的url。
---------------------返回每个章节的url集合。
info_get()函数:输入url,ip池,请求头集,书名。
---------------------将每次的信息保存到本地。
info_get()函数中我定义四个变量a,b,c,d用于判断每个章节是否有信息返回,在代码中有写足够清晰的注释。
这里我讲一下我的思路,在for循环中,我循环的是章节长度的十倍。a,b,c的初始值都是0。
通过索引,url=li_list[a]可以请求每个章节内容,a的自增实现跳到下一个url。但是在大量的请求中也会有无法访问的情况,所以在返回的信息 ’ text1 ‘ 为空的情况a-=1,那么在下一次循环是依旧会访问上次没有结果的url。
文章图片
这里我遇到了一个坑,我在测试爬取的时候会打印a的值用于观察,出现它一直打印同一个章节数‘340’直到循环结束的情况,此时我以为是无法访问了。后来我找到网页对照,发现这个章节本来就没有内容,是空的,所以程序会一直卡在这里。所以我设置了另外两个变量b,c。
1,使用变量b来存放未变化的a,若下次循环b与a相等,说明此次请求没有成功,c++,因为某些页面本身存在错误没有数据,则需要跳过。
2,若c大于10,说明超过十次的请求,都因为一些缘由失败了,则a++,跳过这一章节,同时变量d减一,避免后续跳出循环时出现索引错误
文章图片
最后是变量d,d的初始值设置为章节长度,d = len(li_list),a增加到与d相同时说明此时li_list的所有url都使用完了,那么就需要跳出循环。
然后就是将取出的数据保存了。
文章图片
文章图片
爬取完成,共计用了10分钟左右。
文章图片
import requests
from lxml import etree
from getip import getip
import random
import timeheaders= {"User-Agent":"Mozilla/5.0 (X11;
Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
}
'''
kw输入完成搜索,打印所有的搜索结果
返回选择的书籍的url
'''
def biquge_get(url):
book_info = []
r = requests.get(url =url,
headers = headers,
timeout = 20
)
r.encoding = r.apparent_encoding
html = etree.HTML(r.text)
# 获取搜索结果的书名
bookname = html.xpath('//td[@class = "odd"]/a/text()')
bookauthor = html.xpath('//td[@class = "odd"]/text()')
bookurl = html.xpath('//td[@class = "odd"]/a/@href')
print('搜索结果如下:\n')
a = 1
b = 1
for i in bookname:
print(str(a) + ':', i, '\t作者:', bookauthor[int(b - 1)])
book_info.append([str(a),i,bookurl[a-1]])
a = a + 1
b = b + 2
c = input('请选择你要下载的小说(输入对应书籍的编号):')
book_name = str(bookname[int(c) - 1])
print(book_name, '开始检索章节')
url2 = html.xpath('//td[@class = "odd"]/a/@href')[int(c) - 1]
r.close()
return url2,book_name'''
输入书籍的url,返回每一章节的url
'''
def get_list(url):r = requests.get(url = url,
headers = headers,
timeout = 20)
r.encoding = r.apparent_encoding
html = etree.HTML(r.text)
# 解析章节
li_list = html.xpath('//*[@id="list"]/dl//a/@href')[9:]
return li_list#请求头集
user_agent = [
"Mozilla/5.0 (compatible;
Baiduspider/2.0;
+http://www.baidu.com/search/spider.html)",
"Mozilla/4.0 (compatible;
MSIE 6.0;
Windows NT 5.1;
SV1;
AcooBrowser;
.NET CLR 1.1.4322;
.NET CLR 2.0.50727)",
"Mozilla/4.0 (compatible;
MSIE 7.0;
Windows NT 6.0;
Acoo Browser;
SLCC1;
.NET CLR 2.0.50727;
Media Center PC 5.0;
.NET CLR 3.0.04506)",
"Mozilla/4.0 (compatible;
MSIE 7.0;
AOL 9.5;
AOLBuild 4337.35;
Windows NT 5.1;
.NET CLR 1.1.4322;
.NET CLR 2.0.50727)",
"Mozilla/5.0 (Windows;
U;
MSIE 9.0;
Windows NT 9.0;
en-US)",
"Mozilla/5.0 (compatible;
MSIE 9.0;
Windows NT 6.1;
Win64;
x64;
Trident/5.0;
.NET CLR 3.5.30729;
.NET CLR 3.0.30729;
.NET CLR 2.0.50727;
Media Center PC 6.0)",
"Mozilla/5.0 (compatible;
MSIE 8.0;
Windows NT 6.0;
Trident/4.0;
WOW64;
Trident/4.0;
SLCC2;
.NET CLR 2.0.50727;
.NET CLR 3.5.30729;
.NET CLR 3.0.30729;
.NET CLR 1.0.3705;
.NET CLR 1.1.4322)",
"Mozilla/4.0 (compatible;
MSIE 7.0b;
Windows NT 5.2;
.NET CLR 1.1.4322;
.NET CLR 2.0.50727;
InfoPath.2;
.NET CLR 3.0.04506.30)",
"Mozilla/5.0 (Windows;
U;
Windows NT 5.1;
zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
"Mozilla/5.0 (X11;
U;
Linux;
en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
"Mozilla/5.0 (Windows;
U;
Windows NT 5.1;
en-US;
rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
"Mozilla/5.0 (Windows;
U;
Windows NT 5.1;
zh-CN;
rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
"Mozilla/5.0 (X11;
Linux i686;
U;
) Gecko/20070322 Kazehakase/0.4.5",
"Mozilla/5.0 (X11;
U;
Linux i686;
en-US;
rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
"Mozilla/5.0 (Windows NT 6.1;
WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
"Mozilla/5.0 (Macintosh;
Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
"Opera/9.80 (Macintosh;
Intel Mac OS X 10.6.8;
U;
fr) Presto/2.9.168 Version/11.52"]
'''
参数:url,ip池,请求头集,书名
'''
def info_get(li_list,ip_list,headers,book_name):
print('共计'+str(len(li_list))+'章')
'''
a,用于计数,成功请求到html并完成后续的存写数据才会继续请求下一个url
b,在循环中存放未经过信息返回存储判断的a,用于与下一次循环的a作比较,判断a是否有变化
c,若超过10次b=a,c会自增,则说明应该跳过此章节,同时d减一
d,章节长度
'''
a = 0
b = 0
c = 0
d = len(li_list)
fp = open('./'+str(book_name)+'.txt', 'w', encoding='utf-8')
#这里循环了10倍次数的章节,防止无法爬取完所有的信息。
for i in range(10*len(li_list)):
url = li_list[a]
#判断使用http还是https
if url[4:5] == "s":
proxies = random.choice(ip_list[0])
else:
proxies = random.choice(ip_list[1])
try:
r = requests.get(url=url,
headers={
'User-Agent': random.choice(headers)},
proxies=proxies,
timeout=5
)r.encoding = r.apparent_encoding
r_text = r.text
html = etree.HTML(r_text)
try:
title = html.xpath('/html/body/div/div/div/div/h1/text()')[0]
except:
title = html.xpath('/html/body/div/div/div/div/h1/text()')
text = html.xpath('//*[@id="content"]/p/text()')
text1 = []for i in text:
text1.append(i[2:])'''
使用变量b来存放未变化的a,若下次循环b与a相等,说明此次请求没有成功,c++,因为某些页面本身存在错误没有数据,则需要跳过。
若c大于10,说明超过十次的请求,都因为一些缘由失败了,则a++,跳过这一章节,同时变量d减一,避免后续跳出循环时出现索引错误
'''
if b == a:
c += 1
if c > 10:
a += 1
c = 0
d -=1
b = a#a+1,跳到下一个url,若没有取出信息则a-1.再次请求,若有数据返回则保存
a+=1
if len(text1) ==0:
a-=1
else:
fp.write('第'+str(a+1)+'章'+str(title) + ':\n' +'\t'+str(','.join(text1) + '\n\n'))
print('《'+str(title)+'》','下载成功!')
r.close()except EnvironmentError as e:
pass
# a是作为索引在li_list中取出对应的url,所以最后a的值等于li_list长度-1,并以此为判断标准是否跳出循环。
if a == d:
break
fp.close()if __name__ == '__main__':
kw = input('请输入你要搜索的小说:')
url = f'http://www.b520.cc/modules/article/search.php?searchkey={
kw}'
bookurl,book_name = biquge_get(url)
li_list = get_list(bookurl)
ip_list = getip()
t1 = time.time()
info_get(li_list,ip_list,user_agent,book_name)t2 = time.time()
print('耗时'+str((t2-t1)/60)+'min')
推荐阅读
- python学习之|python学习之 实现QQ自动发送消息
- 逻辑回归的理解与python示例
- python自定义封装带颜色的logging模块
- 【Leetcode/Python】001-Two|【Leetcode/Python】001-Two Sum
- Python基础|Python基础 - 练习1
- Python爬虫|Python爬虫 --- 1.4 正则表达式(re库)
- 使用协程爬取网页,计算网页数据大小
- Python(pathlib模块)
- python青少年编程比赛_第十一届蓝桥杯大赛青少年创意编程组比赛细则
- Python数据分析(一)(Matplotlib使用)