Python 爬取煎蛋网随手拍

百度百科对爬虫的定义为:网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动的抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁,自动索引,模拟程序或者蠕虫。

而我本人对爬虫的理解,就是利用Python下载网站的源代码,并对该源代码进行解析,并提取出我们想要的内容(如文字,图片,音频等)。这听起来好像挺简单的,只要知道网站的地址,并利用resquests模块访问网站并下载源码就可以了。

于是我们可以照着这个初步的思路,就能成功地爬取一些未设置反爬虫技术的网站,比如我另外一篇爬取豆瓣图书目录的文章。今天我们做一个进阶,实现对图片的爬取,以及破解简单的反爬虫技术

首先,我们去目标网站煎蛋网看一下。
在这里插入图片描述
我们先进入首页,但这个网页不是我们要爬取的目标。在导航条中找到随手拍栏目,点进去。
在这里插入图片描述
点进去以后才是我们要爬取的网页,这里面有很多的图片,这就是我们今天的最终爬取目标。Ok,首先我们把这个网址复制下来——http://jandan.net/ooxx。
万丈高楼平地起,我们先试着爬取其中一张图片。
鼠标右键查看源代码,在图片上右键检查元素,就可以在网页代码中定位到该图片的位置。
在这里插入图片描述

我们知道自己要爬取的图片的信息为:

<img style="max-height: none; max-width: 480px;" src="http://ww3.sinaimg.cn/mw600/0073tLPGgy1fxyzdn2l6jj31400u0e83.jpg">

有了以上信息,我们就可以使用

picture=soup.find("img",style="max-height: none; max-width: 480px;",attrs="src")

尝试初步的爬取了。代码如下:

from bs4 import BeautifulSoup
import requests
import base64

#把网址粘贴进来
url="http://jandan.net/ooxx"
#伪装成浏览器
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
#固定格式:访问网站,下载网页代码
req=requests.get(url,headers=headers)
#固定格式:以lxml解释器读取下载的网页文本
soup=BeautifulSoup(req.text,"lxml")#调用lxml解析器,需要导入lxml模块
#找到该图片
picture=soup.find("img",style="max-height: none; max-width: 480px;",attrs="src")
#输出图片地址
print(picture)

理想状态下,这段代码应该输出"http://ww3.sinaimg.cn/mw600/0073tLPGgy1fxyzdn2l6jj31400u0e83.jpg",但是…
在这里插入图片描述
???
为什么会是"None",仔细检查了n遍代码后发现没问题,网站上的源码也没问题。然后再想一下,既然是在我们下载的html文本中没找到我们想要的字段,那就看一下我们下载到的网页文本

print(soup)

在这里插入图片描述
用"逐字逐句对照法"对比我们下载的网页代码和网站源代码。最后我们终于不得不承认那句莫名其妙的

<span class="img-hash">Ly93dzMuc2luYWltZy5jbi9tdzYwMC8wMDZYTkVZN2d5MWZ4ejNma3hxYjJqMzBybzByb2pzeC5qcGc=</span></p>

就是我们要找的图片下载地址,而源代码中的“查看原图”也被替换成了一张blank.gif。

当当当!传说中的反爬虫技术,我们总算是见识到了。然后呢?只差临门一脚就踏入新天地了,结果发现大门紧闭。那就看看门上的锁是怎样的喽?(检索这段乱码)

#picture=soup.find("img",style="max-height: none; max-width: 480px;",attrs="src")
picture=soup.find("span",class_="img-hash")

得到的结果如下:
在这里插入图片描述
关于这段乱码,我有两个猜想:
1.这只是一段纯粹的乱码,网站维护者为了戏耍爬虫而设置的随机请求响应;
2.这段乱码是有规律的,可以通过解码译出图片地址。

对于猜想1,很明显那是绝对不可能的,因为我坚信编程具有严谨性,如果是纯粹的乱码,那普通用户访问该网站时也绝对不可能看到图片。但就算不能真的乱码,如果是基于请求时间进行相应反馈的动态加密数据(C++中随机函数rand()就是根据时间给出随机数的,而百度翻译的反爬虫技术就与此类似),以我目前的技术来看,也跟乱码差不多了。所以为了避免我做徒劳的无用功,我多次爬取了该段代码,发现它是一串静态的数据,那应该就比较容易破解了…吧。

好了,知道了这个锁不是死锁,但我们并不知道锁的内部构造(乱码的生成函数)如何,也没有配锁(编写解码函数)的技术。那怎么办?只能找找看有没有万能钥匙(广泛使用的基础解码函数)了。
结果还真有!(百度大法好啊)

import base64
print(base64.b64decode(picture.text).decode("utf-8"))
#运行得到:
#//ww3.sinaimg.cn/mw600/0073tLPGgy1fxyzdn2l6jj31400u0e83.jpg
#好像不太完整,加个“http:”
print("http"+base64.b64decode(picture.text).decode("utf-8"))
#http://ww3.sinaimg.cn/mw600/0073tLPGgy1fxyzdn2l6jj31400u0e83.jpg

Tips:Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,可以查看RFC2045~RFC2049,上面有MIME的详细规范。Base64编码可用于在HTTP环境下传递较长的标识信息。
有一个bs4加密解密网站:https://base64.supfree.net/。

把结果复制到浏览器粘贴:
在这里插入图片描述

爬到图片地址,接下来就是下载了:
1.先拿出图片地址

src="http"+base64.b64decode(picture.text).decode("utf-8")

2.像访问网页一样访问该网址

img=requests.get(src,headers=headers)

3.像下载txt文本一样,创建路径,在该路径下创建.jpg后缀名文件存储数据。(这里要特别说明一下,在获取图片时,调用img对象的content属性获取二进制文本,而获取txt文本时,调用img对象的text属性获取字符串类型文本。)

import os
path = os.path.join("L:/煎蛋网随手拍")  # 合并打开路径
if not os.path.exists(path):#校验文件是否存在
   os.mkdir(path)
 with open(os.path.join(path, "下载的图片.jpg"), "wb") as f:  # 合并路径path和jpg,不存在则自动创建路径
   f.write(img.content)

4.下载完成。
在这里插入图片描述

既然门都开了,那肯定不能只看完前院就走,我们要整个座房子都看看。爬取它整个网站的图片!
将之前的find(class_=“img-hash”)函数改成find_all(class_=“img-hash”),就能将在当前网站查找"img-hash"值的class标签字段从一个变成全部,并且生成列表存储。
结果如下:
在这里插入图片描述
在这里插入图片描述但是你发现,这样只能下载当前页的图片,整个网站60页的图片我全都要,该怎么办?
那就点一下其他页面看看
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以发现这些网页跳转都是有固定格式的,就最前面那一页没有后缀,那我们点一下第60页
在这里插入图片描述
可以发现它也是符合一般规律,可以有后缀的。
那这样我们就可以写一个字符串内嵌变量的url去做页面循环跳转,但因为网站的图片一直在更新,最大页面数也一直在变,我们怎么得到这个最大页数呢?
哈哈,其实很简单,看下面的操作
在这里插入图片描述
还是检查元素,帮我们找到了这个最大页数,然后

page=int(soup.find("span",class_="current-comment-page").get_text()[1:-1])
print(page)
#运行结果:
#60

然后就可以做一个循环让它去不断跳转页面了

url=url = 'http://jandan.net/ooxx'
page=int(soup.find("span",class_="current-comment-page").get_text()[1:-1])
while page>0:
	real_url=url+"/page-"+str(page)
	#http://jandan.net/ooxx/page-?
	page=page-1

最后,我们把上面那些零件拼装一下,得到我们想要的最终代码:


from bs4  import BeautifulSoup
import requests
import os
import base64

url = 'http://jandan.net/ooxx'
headers={
   'User-Agent':'MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1'
}
#先进入主站
html=requests.get(url,headers)
soup=BeautifulSoup(html.text,"lxml")

#获取总页数
page=int(soup.find("span",class_="current-comment-page").get_text()[1:-1])

#先创建图片存储位置
path = os.path.join("L:/煎蛋网随手拍")  # 合并打开路径
if not os.path.exists(path):#校验文件是否存在
   os.mkdir(path) #创建路径path
num=1#记录图片数
while page>0:
   #每一页的网站地址为 http://jandan.net/ooxx/page-页数
   real_url=url+"/page-"+str(page)

   #读取网页
   html=requests.get(real_url,headers=headers)
   soup=BeautifulSoup(html.text,"lxml")

   # 找到被掩盖的图片地址乱码
   imgurl=soup.find_all(class_="img-hash")

   #译出乱码内的真实地址,别看很复杂的一段,用base64的.decode直接解码,就出现真实网址了
   real_img_url=[]
   for a in imgurl:
      img_url=a.text
      img_url=base64.b64decode(img_url).decode("utf-8")
      real_img_url.append(img_url)

   #下载图片
   for a in real_img_url:
         src="http:"+a#图片地址并不完整,要添加一个http头
         print(src)
         img=requests.get(src,headers=headers)#访问图片页面
         with open(os.path.join(path, str(num)+".jpg"), "wb") as f:  # 合并路径path和num.jpg,不存在则自动创建
            f.write(img.content)  # img.content获取二进制数据,而img.text获取字符串文本
         num=num+1#图片数+1
   page=page-1#页数-1

最后的最后,应该挺详细的了吧,如果还有什么不懂的地方可以在下方评论留言,或者有什么错误的地方也希望各位不吝指教。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章