前些日子发现了一个xkcd的中文站,想把它们都爬下来。
URL:https://xkcd.in/
这里先给大家欣赏一幅:
(图片来自:你最喜欢 xkcd 的哪一张? - 知乎 (zhihu.com))
废话不多说,我们现在就开始吧。
一、单线程下载
1、下载单页图片
我们先下最新的一张:
右键点击图片,打开检查:
发现图片存在class为comic-body的div中。
事实上,我们可以打开页面源代码,发现……
图片存在第一个img标签中。我们可以编写代码:
import requests, os, bs4, urllib
url = 'https://xkcd.in/'
os.makedirs('xkcd',exist_ok = True)
res = requests.get(url)
res.encoding = 'utf-8'
soup = bs4.BeautifulSoup(res.text,'html.parser')
img_tag = soup.img
print(img_tag)
url = 'https://xkcd.in' + img_tag.get('src')
print('url:',url)
urllib.request.urlretrieve(url,'./xkcd/' + img_tag.get('alt') + '.jpg')
下载完成!
2、获取下一个页面
打开开发者模式,发现下一篇的链接存在 class 为 nextLink 的 a 标签里。
这很简单,只需要这样获取:
url = 'https://xkcd.in/' + soup.find('div',class_ = "nextLink").find('a').get('href')
3、整合
把代码整合起来,就可以下载全站漫画了:
import requests, os, bs4, urllib, time
url = 'https://xkcd.in'
os.makedirs('xkcd',exist_ok = True)
while True:
res = requests.get(url)
res.encoding = 'utf-8'
soup = bs4.BeautifulSoup(res.text,'html.parser')
img_tag = soup.img
img_url = 'https://xkcd.in' + img_tag.get('src')
print('正在下载:',img_url)
urllib.request.urlretrieve(img_url,'./xkcd/' + img_tag.get('alt') + '.jpg')
if 'id=1' == url[-4:]: #最小id是1
break
url = 'https://xkcd.in/' + soup.find('div',class_ = "nextLink").find('a').get('href')
input('下载完成')
二、多线程下载
对于多线程,我知道一个很简单的库 —— multitasking 库。
只需要用 pip 安装即可:
pip install multitasking
使用方式:
import time
def main():
time.sleep(1)
start = time.time()
for i in range(5):
main()
end = time.time()
print(end - start)
#输出:5.00570011138916
这段代码要5秒执行。
现在我们用多线程执行:
import time, multitasking
@multitasking.task
def main():
time.sleep(1)
start = time.time()
for i in range(5):
main()
multitasking.wait_for_tasks() # 等待全部线程执行完毕
end = time.time()
print(end - start)
# 输出:1.002472162246704
由于用多线程执行,所以时间少很多。
改编
我们可以建立多个线程,每个线程下载 50 个页面。
我们先编写一个分块函数:
#参考自:https://zhuanlan.zhihu.com/p/369531344
def split_(start, end, step):
# 分多块
parts = [(start, min(start+step, end))
for start in range(0, end, step)]
return parts
再改编一下下载程序:
def d(start_id,end_id):
for id in range(start_id,end_id):
print(id,end = '')
url = 'https://xkcd.in/comic?lg=cn&id='+str(id)
res = requests.get(url)
res.encoding = 'utf-8'
res.raise_for_status() # 返回请求的状态
soup = bs4.BeautifulSoup(res.text,'html.parser')
comicele = soup.find('div',class_ = "comic-body")
if comicele == None:
print(',无图片')
continue
comicele = comicele.find_all('img')[0]
#print(comicele)
if comicele == []:
print('图片未找到')
else:
comicurl = 'https://xkcd.in/' + comicele.get('src')
#res = requests.get(comicurl)
name = soup.find_all('h1')[1].text.replace(' ',"").replace('\r\n',"").replace('\\',"").replace(r"/","").replace(r"/","").replace(':',"").replace(":","").replace("*","").replace('''"''',"").replace('<',"").replace('>',"").replace('|',"")
print('正在下载:',"D:\\python\\漫画爬虫\\xkcd_2\\%s.png"%(name))
urllib.request.urlretrieve(comicurl,"D:\\python\\漫画爬虫\\xkcd_2\\%s.png"%(name)) # urllib.request.urlretrieve函数下载文件。
整合全部代码:
import requests,os,bs4,urllib,multitasking
from time import sleep
import signal
signal.signal(signal.SIGINT, multitasking.killall) # ctrl + c 一次终止已开启的全部线程
@multitasking.task
def d(start_id,end_id):
for id in range(start_id,end_id):
print(id,end = '')
url = 'https://xkcd.in/comic?lg=cn&id='+str(id)
res = requests.get(url)
res.encoding = 'utf-8'
res.raise_for_status() # 返回请求的状态
soup = bs4.BeautifulSoup(res.text,'html.parser')
comicele = soup.find('div',class_ = "comic-body")
if comicele == None:
print(',无图片')
continue
comicele = comicele.find_all('img')[0]
#print(comicele)
if comicele == []:
print('图片未找到')
else:
comicurl = 'https://xkcd.in/' + comicele.get('src')
#res = requests.get(comicurl)
name = img_tag.get('alt')
print('正在下载:',"D:\\python\\漫画爬虫\\xkcd_2\\%s.png"%(name))
urllib.request.urlretrieve(comicurl,"D:\\python\\漫画爬虫\\xkcd_2\\%s.png"%(name)) # urllib.request.urlretrieve函数下载文件。
def get_end_id():
return int(bs4.BeautifulSoup(requests.get('https://xkcd.in/').text,'html.parser').find('div',class_ = "nextLink").find('a').get('href').split('=')[-1]) + 1
def split_(start, end, step):
# 分多块
parts = [(start, min(start+step, end))
for start in range(0, end, step)]
return parts
s = split_(1,get_end_id(),50)
for i in s:
if not i == s[-1]:
d(i[0],i[1])
else:
d(i[0],i[1] + 1)
sleep(5)
multitasking.wait_for_tasks()
input('下载完成')
运行结果:
0,无图片
1正在下载: 桶 - 1.png
2正在下载: “小”树(草图).png
50正在下载: Penny Arcade.png
3正在下载: 岛(草图).png
51正在下载: 疟疾.png
524100正在下载: 秘密世界.png
正在下载: 风景(草图).png
正在下载: 激光瞄准镜.png
正在下载: 爱好.png
10254150正在下载: 回到未来.png
正在下载: 科学.png
,无图片
1516,无图片
152正在下载: 冷幽默.png
55103,无图片
153正在下载: 无用功.png
正在下载: 道德相对论.png
56,无图片
1547104正在下载: 解药(治疗乐队).png
正在下载: 睡觉的女孩(素描-高二西班牙语课).png
,无图片
155,无图片
输出很乱,因为有很多线程来输出。不过下得很快,一下子就下完了。