/downloadDocin

免费自动下载豆丁网的资料Download materials in Docin freely and automatically

Primary LanguagePython

downloadDocin

免费自动下载豆丁网的资料

Download materials in Docin freely and automatically

如遇图片显示不全,请到我的博客观看,欢迎点赞留言!

最近在查找资料时,在豆丁网上找到不少有用的资料,比如说一些课程的PPT之类的,但是只能在线看,而且还有广告,但是下载下来要钱,而且要价不菲,于是动起了歪脑筋,寻思着搞一个爬虫直接下载下来就可以离线看了,也方便资料的存储管理。本教程已完结,请放心食用,效果如下(该效果图采用screentogif软件录制,特此鸣谢):

豆丁网资料自动下载效果展示

本代码免费开源,不想了解原理的可以跳过到使用部分直接使用,希望能给我点个赞以支持开发,如果方便的话,github来一颗星星更好啦!

开源代码地址:https://github.com/you8023/downloadDocin,直接下载按照使用方法使用即可,如遇问题,欢迎在文章下方留言或在github上提issue。

开发环境

  • Windows 10
  • Sublime Text 3
  • Python 3.8
  • python库:lxml、fpdf、requests

环境搭建

  1. 首先安装Python,直接到官网下载安装即可
  2. 安装Python库 键盘同时按下win+R,在弹出的对话框中输入cmd按回车 在弹出的黑框中输入命令安装python库:
pip install lxml
pip install fpdf
pip install requests

至此,环境搭建完毕

分析&设计

在编写代码前,需要对需求及网页进行分析,明确我们需要的东西的位置

需求分析

首先明确需求,我们需要将豆丁网上我们需要的资料爬取下来,通过对页面元素进行观察,发现上面的资料,无论何种格式,均是以图片的形式进行展示,因此,考虑将其保存为pdf以方便查看。提取的输入输出如下:

  • 输入:所需资料的网址
  • 输出:资料的pdf文件,其中包含:
    • 资料的每张图片
    • 资料标题

页面分析

页面标题

打开想要的资料的网页,这里以这个网页为例,首先,鼠标右键检查,找到标题元素:

标题元素

可以发现,标题所属的为span标签,class为doc_title fs_c_76,因此,可以据此定位标题所在

页面资料内容

再以此方法找到资料所属的标签:

网页元素

发现网页的资料均是以图片形式显示,因此,目标确定,我们仅需要找到这个链接即可

复制链接,直接用浏览器打开,发现果然就是我们要找的这张图片

接着,分析链接https://docimg1.docin.com/docinpic.jsp?file=456842624&width=942&sid=LE-fLs-BXk4g4mtVLS2D8apgA9Z5X3NNeoZbh0mHZkW*C1Zz1LvKe8xey1BsO1BG&pageno=1&pcimg=1,发现其中有三个关键字段,分别是:

  • file
  • sid
  • pageno

其中,经过观察,发现file字段的数字和网页链接https://www.docin.com/p-456842624.html的数字一致;

pageno字段则是第几页;

sid经过打开另一个资料的网页测试,发现不同的资料具有不同的sid,观察其编码,没发现规律,最终,经过仔细分析,在source页面源码中找到了一个关键字段flash_param_hzq

flash_param_hzq字段的发现

虽然和sid字段并不完全一致,但使用该字段作为sid也能得到图片,因此,提取该网页的flash_param_hzq字段即可;

代码实现

页面分析完毕,即可开始编写代码。

获取资料标题和内容的通用url

首先,使用requests获取网页内容,并使用lxml将其封装为一个HTML对象方便后续解析:

html = etree.HTML(requests.get(originUrl).text)

然后使用xpath提取标题:

title = html.xpath('//span[@class="doc_title fs_c_76"]/text()')[0]

其中,//span表示匹配任意span标签,使用[@class=""]匹配class属性,使用/text()提取标签内的内容,由于返回的内容为一个元祖,因此使用[0]取第一个元素

使用正则表达式匹配file字段:

fileId = re.findall('\-\d+\.',originUrl)[0][1:-1]

其中,\d代表匹配数字,+表示匹配一次或多次,[1:-1]表示取结果的第二个字符到倒数第二个字符

将HTML对象转为字符串:

theHTML = etree.tostring(html).decode('utf-8')

使用正则表达式匹配flash_param_hzq字段:

sid = re.findall('flash_param_hzq:\"[\w\*\-]+\"', theHTML)[0][17:-1]

其中,\w表示匹配数字或字母

至此,该部分函数书写完毕,完整代码为:

def getTiltleUrl(originUrl):
	html = etree.HTML(requests.get(originUrl).text)
	theHTML = etree.tostring(html).decode('utf-8')
	# print(theHTML)
	try:
		title = html.xpath('//span[@class="doc_title fs_c_76"]/text()')[0]
	except:
		title = html.xpath('//title/text()')
	fileId = re.findall('\-\d+\.',originUrl)[0][1:-1]

	sid = re.findall('flash_param_hzq:\"[\w\*\-]+\"', theHTML)[0][17:-1]
	url = 'https://docimg1.docin.com/docinpic.jsp?file=' + fileId + '&width=1000&sid=' + sid + '&pcimg=1&pageno='
	return title, url

获取图片

通过上面的函数获取到通用的链接后,仅需要更改pageno字段即可获取所有图片,使用requests获取到图片后,直接将文件流写入到文件中即可。但在后续代码运行过程中发现图片格式报错,因此,使用PIL标准化图片。完整代码如下:

def getPictures(theurl, path):
	pagenum = 1
	headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"
	}
	allNum = 0
	while pagenum>0:
		# time.sleep(3*random.random())
		print('Downloading picture ' + str(pagenum))
		url = theurl + str(pagenum)
		img_req = requests.get(url=url, headers=headers)
		if img_req.content==b'sid error or Invalid!':
			allNum = pagenum-1
			print('Downloading finished, the count of all pictures is ' + str(allNum))
			pagenum = -1
			break;
		file_name = path + str(pagenum) + '.png'
		f = open(file_name, 'wb')
		f.write(img_req.content)
		f.close()
		# 将图片保存为标准png格式
		im = Image.open(file_name)
		im.save(file_name)
		pagenum += 1
	return allNum

合并图片为pdf

这里主要使用fpdf库合并图片为pdf,代码如下:

def combinePictures2Pdf(path, pdfName, allNum):
	print('Start combining the pictures...')
	pagenum = 1
	file_name = path + str(pagenum) + '.png'
	cover = Image.open(file_name)
	width, height = cover.size
	pdf = FPDF(unit = "pt", format = [width, height])
	while allNum>=pagenum:
		try:
			print('combining picture ' + str(pagenum))
			file_name = path + str(pagenum) + '.png'
			pdf.add_page()
			pdf.image(file_name, 0, 0)
			pagenum += 1
		except Exception as e:
			print(e)
			break;
	pdf.output(pdfName, "F")

其中:

  • pdf = FPDF(unit = "pt", format = [width, height])规定了pdf的尺寸
  • pdf.add_page()将为pdf添加一张空白页面
  • pdf.image(file_name, 0, 0)则是将图片绘制到该空白页面上,后两个参数为绘制的起始xy坐标
  • pdf.output(pdfName, "F")则是生成pdf文件

删除原图片

pdf生成完毕之后,之前保存到本地的图片就没有用武之地了,这时需要删去所有图片,删去某个文件的语句为:

os.remove(file_name)

完整代码如下:

def removePictures(path, allNum):
	pagenum = 1
	while allNum>=pagenum:
		try:
			print('deleting picture ' + str(pagenum))
			file_name = path + str(pagenum) + '.png'
			os.remove(file_name)
			pagenum += 1
		except Exception as e:
			print(e)
			break;

主函数

最后,书写语句依次调用函数,自动化下载图片,合并为pdf,并删去原文件:

if __name__ == '__main__':
	path = 'E:\\test\\Docin\\'
	# originUrl = 'https://www.docin.com/p-977106193.html?docfrom=rrela'
	originUrl = input('input the url: ')
	result = getTiltleUrl(originUrl)
	title = result[0].split('.')[0]
	url = result[1]
	print(title, url)
	allNum = getPictures(url, path)
	pdfName = title + '.pdf'
	combinePictures2Pdf(path, pdfName, allNum)
	removePictures(path, allNum)

其中:

  • path为保存文件的路径,注意,必须为转义\后的绝对路径
  • 原网址使用input函数以让用户自行输入

代码使用

源码在GitHub上,直接下载即可使用。

  1. 进入到源码所在的文件夹,使用编辑器(记事本亦可)打开源码文件,修改最下方main函数中的path路径为你想要保存文件的路径
  2. 在文件所在的文件夹的地址栏输入cmd,按下回车,在出现的黑框中输入以下命令:
python downloadPPT.py
  1. 按下回车,当出现提示语句时输入网址,按下回车
  2. 静等程序跑完即可,下载的资料在第一步输入的路径里面

如图所示: 豆丁网资料自动下载效果展示