好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Python网络爬虫实战:世纪佳缘爬取近6万条小姐姐数据后发现惊天秘密

又是一年双十一了,不知道从什么时候开始,双十一从“光棍节”变成了“双十一购物狂欢节”,最后一个属于单身狗的节日也成功被攻陷,成为了情侣们送礼物秀恩爱的节日。

翻着安静到死寂的聊天列表,我忽然惊醒,不行,我们不能这样下去,光羡慕别人有什么用,我们要行动起来,去找自己的幸福!!!

我也想“谈不分手的恋爱” !!!内牛满面!!!

注册登陆一气呵成~

筛选条件,嗯...性别女,年龄...18到24岁,身高嘛,无所谓啦,就按默认155-170吧,地区...嗯北京好,北京近一点,照片?那肯定要啊,必须的!!!

小姐姐们我来了~

哇,好多小姐姐啊,到底该选哪个搭讪啊.......

这时候就该我们的爬虫出场了

爬虫部分

爬虫部分还是我们之前的四步:分析目标网页,获取网页内容,提取关键信息,输出保存

1. 首先分析目标网页

按F12召唤开发者工具页面,切换到Network选项,然后在翻页的时候抓包,成功截获请求URL。

不过,这个请求是 POST 方法的,和我们之前见到的 GET 方法的请求有点不同。哪里不同呢......它有点短?

没错,它太短了,少了很多信息,比如我们搜索的条件,甚至连页码的信息都没有,光靠它怎么可能正确的找到小姐姐嘛!

别急,往下翻,其实,?POST 方法请求的参数是放在 Form Data 的地方(怎么可能没有嘛是吧)

PS:除了这些之外,还有更多更细的筛选条件

更多筛选条件及其对应的编号 1 2 3 4 5 6 7 8 9 10 地区 年龄 身高 学历 月薪 婚史 购房 购车 籍贯 户口 11 12 12 14 15 16 17 18 22 23 民族 宗教信仰 有无子女 职业 公司类型 生肖 星座 血型 诚信等级 照片

构造完整的请求的 URL ,?

http://search.jiayuan测试数据/v2/search_v2.php?key=&sex=f&stc=1:11,2:18.24,3:155.170,23:1&sn=default&sv=1&p=1&f=select

访问,没问题,改变p的值,访问,没问题,OK本阶段完成。

2.??解析网页内容

通过上面的url,我们可以获取到服务器返回的 json格式的用户信息。代码如下:

import?requestsdef?fetchURL(url):
????'''
????功能:访问?url?的网页,获取网页内容并返回
????参数:
????????url?:目标网页的?url
????返回:目标网页的?html?内容
????'''
????headers?=?{????????'accept':?'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',????????'user-agent':?'Mozilla/5.0?(Windows?NT?10.0;?WOW64)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/68.0.3440.106?Safari/537.36',????????'Cookie':?'guider_quick_search=on;?SESSION_HASH=f09e081981a0b33c26d705c2f3f82e8f495a7b56;?PHPSESSID=e29e59d3eacad9c6d809b9536181b5b4;?is_searchv2=1;?save_jy_login_name=18511431317;?_gscu_1380850711=416803627ubhq917;?stadate1=183524746;?myloc=11%7C1101;?myage=23;?mysex=m;?myuid=183524746;?myincome=30;?COMMON_HASH=4eb61151a289c408a92ea8f4c6fabea6;?sl_jumper=%26cou%3D17%26omsg%3D0%26dia%3D0%26lst%3D2018-11-07;?last_login_time=1541680402;?upt=4mGnV9e6yqDoj%2AYFb0HCpSHd%2AYI3QGoganAnz59E44s4XkzQZ%2AWDMsf5rroYqRjaqWTemZZim0CfY82DFak-;?user_attr=000000;?main_search:184524746=%7C%7C%7C00;?user_access=1;?PROFILE=184524746%3ASmartHe%3Am%3Aimages1.jyimg测试数据%2Fw4%2Fglobal%2Fi%3A0%3A%3A1%3Azwzp_m.jpg%3A1%3A1%3A50%3A10;?pop_avatar=1;?RAW_HASH=n%2AazUTWUS0GYo8ZctR5CKRgVKDnhyNymEBbT2OXyl07tRdZ9PAsEOtWx3s8I5YIF5MWb0z30oe-qBeUo6svsjhlzdf-n8coBNKnSzhxLugttBIs.;?pop_time=1541680493356'
????}????try:
????????r?=?requests.get(url,?headers=headers)
????????r.raise_for_status()
????????r.encoding?=?'unicode_escape'
????????print(r.url)????????return?r.text????except?requests.HTTPError?as?e:
????????print(e)
????????print("HTTPError")????except?requests.RequestException?as?e:
????????print(e)????except:
????????print("Unknown?Error?!")if?__name__?==?'__main__':
????url?=?'http://search.jiayuan测试数据/v2/search_v2.php?key=&sex=f&stc=2:18.24,3:155.170,23:1&sn=default&sv=1&p=1&f=select'
????????html?=?fetchURL(url)
????????print(html)

?在这里有几个需要注意的点:

① 网站的搜索功能是需要登陆的,否则会一直弹框提示登陆,所以我只好注册了个账号,登陆后将 cookie 放入爬虫的请求头中,这样便可正确访问数据(不过在爬虫爬取过程中,我发现其实不需要登陆也可以获取,我是不是亏了?!!)

②直接打开请求返回的结果,其实是不能“看”的,通篇没一个汉字,都是各种类似于乱码的东西,这其实是一种?unicode 编码,将汉字转化成 \u 开头的一串字符。

想要解码成汉字也很容易,只需要将 response 的编码方式?encoding 设定为?unicode_escape 即可。

r?=?requests.get(url,?headers=headers)
r.raise_for_status()
r.encoding?=?'unicode_escape'print(r.text)

?③为了寻找尽可能多一点的小姐姐,我决定将“地区”这一条件限制去掉,重新访问,果然,搜到的小姐姐由原先的 3229 瞬间涨到了 59146 ,这下可以开心的爬了。

3.提取关键信息

通过分析上面获取到的?josn 文件,我们可以知道,这里面包含了用户的相当多的信息,包括用户ID,昵称,性别,年龄,身高,照片,学历,城市,择偶标准,以及个性宣言等(不过有些信息在这里是获取不到的,需要进入个人主页才能查看,比如收入等)。

import?jsondef?parserHtml(html):
????'''
????功能:根据参数?html?给定的内存型?HTML?文件,尝试解析其结构,获取所需内容
????参数:
????????????html:类似文件的内存?HTML?文本对象
????'''
????s?=?json.loads(html)
????
????usrinfo?=?[]????
????for?key?in?s['userInfo']:
?????????????
????????blist?=?[]

????????uid?=?key['uid']
????????nickname?=?key['nickname']
????????sex?=?key['sex']
????????age?=?key['age']
????????work_location?=?key['work_location']
????????height?=?key['height']
????????education?=?key['education']
????????
????????matchCondition?=?key['matchCondition']
????????marriage?=?key['marriage']
????????income?=?key['income']
????????shortnote?=?key['shortnote']
????????image?=?key['image']

????????blist.append(uid)
????????blist.append(nickname)
????????blist.append(sex)
????????blist.append(age)
????????blist.append(work_location)
????????blist.append(height)
????????blist.append(education)
????????blist.append(matchCondition)
????????blist.append(marriage)
????????blist.append(income)
????????blist.append(shortnote)
????????blist.append(image)
????????
????????usrinfo.append(blist)
????????print(nickname,age,work_location)????#writePage(usrinfo)
????print('---'?*?20)

部分运行结果如下:

http://search.jiayuan测试数据/v2/search_v2.php?key=&sex=f&stc=2:18.24,3:155.170,23:1&sn=default&sv=1&p=1&f=select
小文文?23?西安
还单身的糖豆?18?济南
小了白了兔?24?怀化
小雅xy?24?深圳
惜梦缘?23?杭州
冬季?24?南京
qian141?21?石家庄
浪人心伤?24?闵行
高挑的柠檬?24?长沙
甜甜?23?郑州
------------------------------------------------------------
http://search.jiayuan测试数据/v2/search_v2.php?key=&sex=f&stc=2:18.24,3:155.170,23:1&sn=default&sv=1&p=2&f=select
蘑菇?24?武汉
低调宅女?24?南京
女士?21?南京
远处的一抹光?23?南京
娜娜?24?河北
我最喜欢你?24?洛阳
爱笑的香菇?24?广州
LLS?24?惠州
值得?24?无锡
宿媛?23?江北
------------------------------------------------------------

4. 保存输出文件

最后,只需要把提取出的信息写入csv文件,即可完成本次爬取的工作。下面是完整代码:

import?requestsimport?jsonimport?timedef?fetchURL(url):
????'''
????功能:访问?url?的网页,获取网页内容并返回
????参数:
????????url?:目标网页的?url
????返回:目标网页的?html?内容
????'''
????headers?=?{????????'accept':?'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',????????'user-agent':?'Mozilla/5.0?(Windows?NT?10.0;?WOW64)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/68.0.3440.106?Safari/537.36',????????'Cookie':?'guider_quick_search=on;?SESSION_HASH=f09e081981a0b33c26d705c2f3f82e8f495a7b56;?PHPSESSID=e29e59d3eacad9c6d809b9536181b5b4;?is_searchv2=1;?save_jy_login_name=18511431317;?_gscu_1380850711=416803627ubhq917;?stadate1=183524746;?myloc=11%7C1101;?myage=23;?mysex=m;?myuid=183524746;?myincome=30;?COMMON_HASH=4eb61151a289c408a92ea8f4c6fabea6;?sl_jumper=%26cou%3D17%26omsg%3D0%26dia%3D0%26lst%3D2018-11-07;?last_login_time=1541680402;?upt=4mGnV9e6yqDoj%2AYFb0HCpSHd%2AYI3QGoganAnz59E44s4XkzQZ%2AWDMsf5rroYqRjaqWTemZZim0CfY82DFak-;?user_attr=000000;?main_search:184524746=%7C%7C%7C00;?user_access=1;?PROFILE=184524746%3ASmartHe%3Am%3Aimages1.jyimg测试数据%2Fw4%2Fglobal%2Fi%3A0%3A%3A1%3Azwzp_m.jpg%3A1%3A1%3A50%3A10;?pop_avatar=1;?RAW_HASH=n%2AazUTWUS0GYo8ZctR5CKRgVKDnhyNymEBbT2OXyl07tRdZ9PAsEOtWx3s8I5YIF5MWb0z30oe-qBeUo6svsjhlzdf-n8coBNKnSzhxLugttBIs.;?pop_time=1541680493356'
????}????try:
????????r?=?requests.get(url,?headers=headers)
????????r.raise_for_status()
????????r.encoding?=?'unicode_escape'
????????print(r.url)????????return?r.text????except?requests.HTTPError?as?e:
????????print(e)
????????print("HTTPError")????except?requests.RequestException?as?e:
????????print(e)????except:
????????print("Unknown?Error?!")def?parserHtml(html):
????'''
????功能:根据参数?html?给定的内存型?HTML?文件,尝试解析其结构,获取所需内容
????参数:
????????????html:类似文件的内存?HTML?文本对象
????'''
????s?=?json.loads(html)
????
????usrinfo?=?[]????
????for?key?in?s['userInfo']:
?????????????
????????blist?=?[]

????????uid?=?key['uid']
????????nickname?=?key['nickname']
????????sex?=?key['sex']
????????age?=?key['age']
????????work_location?=?key['work_location']
????????height?=?key['height']
????????education?=?key['education']
????????
????????matchCondition?=?key['matchCondition']
????????marriage?=?key['marriage']
????????income?=?key['income']
????????shortnote?=?key['shortnote']
????????image?=?key['image']

????????blist.append(uid)
????????blist.append(nickname)
????????blist.append(sex)
????????blist.append(age)
????????blist.append(work_location)
????????blist.append(height)
????????blist.append(education)
????????blist.append(matchCondition)
????????blist.append(marriage)
????????blist.append(income)
????????blist.append(shortnote)
????????blist.append(image)
????????
????????usrinfo.append(blist)
????????print(nickname,age,work_location)

????writePage(usrinfo)
????print('---'?*?20)def?writePage(urating):
????'''
????????Function?:?To?write?the?content?of?html?into?a?local?file
????????html?:?The?response?content
????????filename?:?the?local?filename?to?be?used?stored?the?response
????'''

????import?pandas?as?pd
????dataframe?=?pd.DataFrame(urating)
????dataframe.to_csv('Jiayuan_UserInfo.csv',?mode='a',?index=False,?sep=',',?header=False)if?__name__?==?'__main__':????for?page?in?range(1,?5916):
????????url?=?'http://search.jiayuan测试数据/v2/search_v2.php?key=&sex=f&stc=2:18.24,3:155.170,23:1&sn=default&sv=1&p=%s&f=select'?%?str(page)
????????html?=?fetchURL(url)
????????parserHtml(html)????????#?为了降低被封ip的风险,每爬100页便歇5秒。
????????if?page%100==99:
????????????time.sleep(5)

后续工作

1. 文件去重

?花了两个多小时,爬了5千多页,爬到了接近六万条数据,本来是一个相当开心的事情,但是当我打开文件,按 用户ID 排序之后,发现!!!!

神魔鬼!!!居然有大量重复的数据,单就这个叫 “名芳” 的用户,便有两千多条,这能得了?!!!

为了验证是不是我程序哪儿出错了,我反复检查调试了很久。

我发现,如果只爬前100页的数据,则重复率较低,而100页之后,便开始大量的出现重复用户了;而且重复的数据并不是同一页中连续出现,而是来自不同页。

百思不得其解,遂求助大佬,大佬听完我的描述之后说,会不会是网站的数据本身便是有问题的?

为了解开这个疑惑,我决定去网站上手动查找,一探究竟,到底在100页之后,发生了什么事儿。

此网站的翻页功能用的相当蹩脚,只有首页,上一页和下一页,页码跳转也每次只能选择前后5页。点了好久终于到了一百多页之后,发现了一件令人震惊的事情。

这是我随手截的三页的截图,108页,109页,和110页,图片下方有截图为证,来感受一下,跟连连看似的。

?好吧,同样的用户换个顺序来凑页数是吗,我现在就想看看去重之后究竟还剩多少个。

import?pandas?as?pd

inputfile?=?"Jiayuan_UserInfo.csv"outputfile?=?"Jiayuan_UserInfo_output.csv"df?=?pd.read_csv(inputfile,encoding='utf-8',names=['uid','nickname','sex','age','work_location','height','education','matchCondition','marriage','income','shortnote','image'])

datalist?=?df.drop_duplicates()
datalist.to_csv(outputfile,encoding='utf-8',index=False,?header=False)
print("Done!")

下面是运行结果:

?堂堂接近6万人的搜索结果,去重之后居然只剩下不到1000人,再回头看看看看网站上 “有 59352?人符合条件”,是不是感觉啪啪打脸呢。用这种手段来营造用户量很大的假象,高明的很呐。

?2.照片下载

?抛开数据作假不说,我们此行的目的可是看小姐姐来的呢!

言归正传,我们来下载小姐姐们的照片咯,照片的链接在我们之前保存的csv文件中就有。

import?requestsimport?pandas?as?pd#?读取csv文件userData?=?pd.read_csv("Jiayuan_UserInfo_output.csv",names=['uid','nickname','sex','age','work_location','height','education','matchCondition','marriage','income','shortnote','image'])for?line?in?range(len(userData)):
????url?=?userData['image'][line]
????img?=?requests.get(url).content
????nickname?=?re.sub("[\s+\.\!\/_,$%^*(+\"\'?|]+|[+——!,。?、~@#¥%……&*()▌]+",?"",userData['nickname'][line])
????
????filename?=?str(line)?+?'-'?+?nickname?+?'-'?+?str(userData['height'][line])?+?'-'?+?str(userData['age'][line])?+?'.jpg'
????try:????????with?open('images_output/'?+?filename,?'wb')?as?f:
????????????f.write(img)????except:
????????print(filename)

print("Finished!")

为了方便辨认,我这里将 序号 + 用户昵称 + 身高 + 年龄 作为图片的文件名

这里有一点需要注意的是,用户昵称中可能会包含一些奇形怪状的字符,以它们作为文件名的话,在保存文件的时候会出现异常,所以这里先将用户名做了一些处理,剔除其中的标点符号等字符,并且做了一个异常处理,即如果出现异常,则输出该文件名,并继续保存下一个。

于是乎,我得到了 996 张小姐姐的照片。。。

不过,这些已经不重要了,

这次,我不仅顺利爬到了全部小姐姐的数据,还发现了网站的一点“小秘密”。

此时的我无比的膨胀,我觉得我很牛逼

我觉得她们都配不上我。

?如果文章中有哪里没有讲明白,或者讲解有误的地方,欢迎在评论区批评指正,或者扫描下面的二维码,加我微信,大家一起学习交流,共同进步。

查看更多关于Python网络爬虫实战:世纪佳缘爬取近6万条小姐姐数据后发现惊天秘密的详细内容...

  阅读:16次