又是一年双十一了,不知道从什么时候开始,双十一从“光棍节”变成了“双十一购物狂欢节”,最后一个属于单身狗的节日也成功被攻陷,成为了情侣们送礼物秀恩爱的节日。
翻着安静到死寂的聊天列表,我忽然惊醒,不行,我们不能这样下去,光羡慕别人有什么用,我们要行动起来,去找自己的幸福!!!
我也想“谈不分手的恋爱” !!!内牛满面!!!
注册登陆一气呵成~
筛选条件,嗯...性别女,年龄...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万条小姐姐数据后发现惊天秘密的详细内容...