点击上方「蓝字」关注我
自律的最高境界就是享受孤独。
俗话说得好
碎碎念
昨天跟小伙伴们聊天,说到当下严峻的局势和作为应届生的我们该如何去面对?是硬着头皮砥砺前行?还是退一步去考研考公,给自己留下更多的时间和空间喘下气?其实无论怎么样都是因人而异,一个猴子有一个栓法;我喜欢吃青菜,但人家就想吃胡萝卜,这无可厚非,条条道路通罗马,看你怎么走而已!
回顾自己,一路走来都是那么 “另类”,我特别喜欢一首歌《Stand out, Fit in》,中文意思很霸气:生来为了出众,何必费力合群!
我一直都认为特立独行,剑走偏锋的道路从来都是人迹罕至且充满荆棘的,走自己的路,别人夸你别信,别人骂你别听,要做到宠辱不惊;做好当下的事情,去追逐所立下的志向,面对别人的异样的眼光和闲言碎语,哈哈一笑就过去了,时刻记住一句话:在你没有成功之前,你一切的才华都是狗屎,只有你成功了,别人才会重视你,哪怕你打个喷嚏,在别人眼里都是何等的帅气。
所以,在此之前,你要做到以下两点:第一个是忍,小不忍则乱大谋,作为一个家族没背景,没势力,祖上几代都是穷苦农民的农村小伙子来说,在没有成功之前,唯有忍,踏踏实实做事,别无他法。第二是熬,天下事,有所利有所贪者成其半,有所激有所逼者成其半。
用你默默的付出换来实实在在的回报,用结果去打脸那些冷嘲热讽,然后静静地等待幸福来敲门吧。
猪头(一脸无所谓):瞎逼逼够了没有,我是看见爬虫才进来的,结果一进来就被你强灌鸡汤,你几个意思???
匠(挠挠头):嘿嘿,姓猪的惹不起啊!!!
前言
我有个舍友,精通收集各种 “小视频” 和 “高清图片”,还专门买了个外置硬盘来存放这些 “珍品”,有时候我们开玩笑说:毕业后我们强强联手,他出资源,我出技术,然后我们搞一个网站。。。
有时候看他大晚上趴在电脑前一张一张的将图片保存到本地(虽然辛苦,但是怡然自得),我就想着给他写个脚本,把他那个成年网站的图片都爬下来,然后再写个图像识别,把符合他 “口味” 的图片甄选出来。。。老实说,像我这样贴心的舍友应该不多吧,哈哈。
分页和瀑布流
本文就以爬取百度图片为例演示吧,就不拿什么成年网站了,在进入正题之前先了解下当前网站加载数据的两种主要方式:分页和瀑布流。
分页很好理解,就是用户可以一页一页地跳转显示数据。
瀑布流,又称为瀑布流式布局。是比较流行的网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。
本文主要使用分页式的爬取方式。
前期分析
先对比下每一页的 URL 有什么区别
第一页:https://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word=%E7%8C%AB&pn=0&gsm=64&ct=&ic=0&lm=-1&width=0&height=0第二页:https://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word=%E7%8C%AB&pn=20&gsm=3c&ct=&ic=0&lm=-1&width=0&height=0第三页:https://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word=%E7%8C%AB&pn=40&gsm=50&ct=&ic=0&lm=-1&width=0&height=0
经对比发现,参数 word=%E7%8C%AB 中的 %E7%8C%AB 其实就是我们搜索关键字的 URL 编码形式,如:“猫” 的 URL 编码就是 %E7%8C%AB。
参数 pn 中,第一页 pn =0,第二页 pn=20,第三页 pn=40 可以得到 pn=(页数-1)*20
其他的参数是图片的一些属性,长宽高,编码形式等等,保持原样即可,区别不大。不影响爬取图片的整体效果。
接下来便是重点了,怎么去找到图片并把图片自动下载到本地呢?
打开 “开发者工具”(快捷键 f12)审查元素,点击图片都可以看到每一张图片在 img 标签中都对应着一个 URL。
但是爬虫一般是和源代码打交道,所以我们还要看图片对应着的 URL 在源代码的什么位置,处于什么 HTML 标签之下。
在页面的源代码按 CTRL+f 使用页面查找功能,随便将一个图片的 URL 粘贴进去查找,发现共出现了三个位置,thumbURL,middleURL,hoverURL
通过多试了几张图片对比发现图片连接就只出现在这三个标签下,随便选取一个标签可做正则匹配,这里选择 middleURL。
技术实现
首先要解决URL编码问题,由于百度图片搜索是以 GET 方式提交参数进行请求,所以会经过浏览器 URL 编码,由于爬虫的流量没有经过浏览器所以需要自行编码然后才能正常请求。先了解下浏览器 URL 编码。
URL 编码是一种浏览器用来打包表单输入的格式。浏览器从表单中获取所有的 name和其中的值 ,将它们以 name/value 参数编码(移去那些不能传送的字符,将数据排行等等)作为 URL 的一部分或者分离地发给服务器。
URL 编码的规则就是对于任何特殊的字符(就是那些不是简单的七位 ASCII,如汉字)将以百分符 % 用十六进制编码,如这里的猫就会进行URL 编码为 %E7%8C%AB
当然,我们是希望能自己输入中文然后脚本自动帮我们编码后拼接到 URL 中去,这样才智能。
这里使用 python 的模块 urllib 来解决 URL 编码问题。
import urllib.parsestring = '猫'url_encode = urllib.parse.quote(string) # 默认是 utf-8 编码print('%s 的 URL 编码是 %s' % (string, url_encode))
C:\Users\Lin\Desktop>python3 test.py猫 的 URL 编码是 %E7%8C%AB
好,接下来就是爬取源代码中所有我们需求的图片的链接,这里以爬取第一页为例。
import requestsimport re# 第一页的 URLurl = 'https://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word=%E7%8C%AB&pn=0&gsm=64&ct=&ic=0&lm=-1&width=0&height=0'header = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0'} # 一般都会带上一个代表浏览器的 header,以免爬虫被禁掉res = requests.get(url=url, headers=header) # 返回一个响应对象html = res.content.decode() # byte -- strfind = '"middleURL":"(.*?)"' # 正则表达式,非贪婪匹配url_lists = re.findall(find, html, re.S) # re.S 表示多行匹配,findall 将匹配的结果用列表保存print(url_lists)
看起来是爬取成功了。
接下来是将爬取图片的链接所对应的内容下载到本地。
for i, url in enumerate(url_lists): res = requests.get(url=url, headers=header) image_name = '%d' % (i + 1) + '.jpg' with open('image/' + image_name, 'wb') as f: f.write(res.content)print('爬取完毕,共 %d 张图片。' % (i + 1))
好了,大致的技术点差不多也就这些,但为了让这工具更智能化,我们会提供用户输入关键字,需要爬取的页数(批量爬取),还要使用多线程来提高爬取图片的速度,多线程的知识在前面已有文章介绍了,详情请看《端口扫描器之python多线程》
整体代码
下面直接上整体代码吧,都有注释,很容易看懂。
import requestsimport urllib.parseimport urllib3import reimport queueimport threadingimport timeurllib3.disable_warnings() # 禁止出现无证书警告pic_urls = [] # 保存图片连接header = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0'}def Get_Queue(url_list): url_queue = queue.Queue(999999) for u in url_list: url_queue.put(u) return url_queuedef GetUrlList(keyword, pages): global pic_urls keyword = urllib.parse.quote(keyword) # 给关键字进行 URL 编码 for page in range(pages): url = 'https://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word={}&pn={}&gsm=64&ct=&ic=0&lm=-1&width=0&height=0'.format( keyword, page * 20) res = requests.get(url=url, headers=header, verify=False) # verify=False 设置无证书验证 html = res.content.decode() find = '"middleURL":"(.*?)"' # 正则表达式 tmp_urls = re.findall(find, html, re.S) # findall 匹配整体,并将匹配的所有结果保存在列表 pic_urls += tmp_urls# 自定义线程类class PicThread(threading.Thread): def __init__(self, pic_queue): threading.Thread.__init__(self) self.pic_queue = pic_queue def run(self): # 重写 run 方法 while not self.pic_queue.empty(): url = self.pic_queue.get() html = requests.get(url=url, headers=header) image_name = str(time.time()) + '.jpg' # 以时间戳重命名文件 with open('image/' + image_name, 'wb') as f: f.write(html.content)def main(): keyword = input('\n请输入搜索关键词:') # 使用input接收的输入是 str,需要使用 int() 转为 int 型 pages = int(input('\n一页60张图,你要爬多少页:')) t = int(input('\n使用的线程数:')) GetUrlList(keyword, pages) # 获取图片链接列表 URLQueue = Get_Queue(pic_urls) # 将列表转换为队列 pic_thread = [] # 线程列表 start_time = time.time() for i in range(t): pic = PicThread(URLQueue) # 初始化一个线程对象 pic_thread.append(pic) for pic in pic_thread: pic.start() for pic in pic_thread: pic.join() print('爬取完毕!%d 张图片,总用时 %d seconds' % (len(pic_urls), time.time() - start_time))if __name__ == '__main__': main()
对于耗时操作,如爬虫,IO操作,使用多线程再合适不过,爬取速度6到飞起。
尾声
猪头(俏脸一红):那我要是输入那些个的关键字,那不就。。。
匠:咳咳,嗯,以上所涉及的技术切莫用于非法途径,如若不从,后果自负,与本人无关。。。
我是匠心,一个在清流旁默默磨剑的匠人,期待那一天能利剑出鞘,仗剑走江湖。