找回密码
 会员注册
查看: 24|回复: 0

Python筛选志愿深圳活动(异步加载爬虫实战)

[复制链接]

6

主题

0

回帖

19

积分

新手上路

积分
19
发表于 2024-9-9 14:40:24 | 显示全部楼层 |阅读模式
引入事情是这样的,最近我和我同学两人要去做志愿活动,希望能一天内做两个,一个在上午,一个下午,并且日期要在18号所以筛选条件就是:①时间为上午(或下午)②日期为2024.8.18③15岁的我们要能参加,也就是说允许做义工的年龄包含15岁④可招募人数>=2人写程序时是13号,当时距离18号还有很久,首页基本上都是最近几天的活动,所以一个一个翻显然不现实,因此,打算写一个程序帮我完成网站分析初步分析网页进入志愿深圳查询志愿项目页面,发现每个项目右下角都有一串日期,这个可以帮助进行初步筛选。往下滑动网页,发现会加载出更多的活动,说明网站使用了异步加载技术,那么我们就无法通过直接请求网页,然后用xpath定位的方式实现筛选了,而是要抓取并分析其请求的数据。一般来说,这些数据的格式为JSON,所以如果能抓取到,那么分析就易如反掌了抓取数据按F12调出开发者工具,选择网络(某些浏览器中叫Network,edge则是一个图标),选择XHR,最后往下滑动网页发现多了一条请求,点进去看看发现这个请求返回了20个活动,以及活动的一些信息分析URL接下来使用Postman分析该请求的请求URL(也可以不用Postman,直接瞪眼法+手动删减URL再用浏览器尝试请求也行) 发现它有15个参数,但是有8个是空的。把这8个参数删除,再请求一次,看看会不会影响结果可以发现并没有影响再删,发现_sw1、lat、lng的存在与否也对结果没有影响接下来就要分析其他参数的作用了。参数_c、_a应该是查询活动的固定参数,不用管,剩下的_p、_ps对结果有影响,前者是页数,后者是每页的活动数。所以我们的程序就可以将_ps固定,将_p不断+1,实现翻页的效果所以最终URL就被缩减为:https://www.sva.org.cn/Default.aspx?_c=ProgramSearch&_a=GetPrograms&_p={p}&_ps={ps}分析数据将_p设为1,_ps设为2(方便分析),将链接复制到浏览器访问,并复制返回结果粘贴到JSON解析工具(方便分析)可以发现,返回结果是一个大字典,其中包含5个键,我们要找的项目就在Programs里面。而Programs的值是一个列表,每个列表又包含若干个字典(字典个数取决于参数_ps的值),每个字典中有其所对应项目的一些信息但是这里面的信息并不完整,我们只能从中找到活动日期这一我们需要的信息,至于年龄限制这些全都找不到所以我们继续分析网页进一步分析网页回到志愿活动查询页面,随便找一个活动点击进去,能看到详细信息,所以我们需要在这个网页内进行数据抓取和分析抓取数据与上文相同,此处不再赘述最终得到URL为https://www.sva.org.cn/Default.aspx?_c=Program&_a=GetProfile&rogramID=f2da36200f38a8b2669767f63ab70291分析URL参数_c、_a老朋友了,不管。新出现了一个参数ProgramID看着就很重要,显然它是数据库定位到指定活动的凭据。而ProgramID也是初步分析网页后可得的,它是键ProID的值。所以我们的程序只需要在初步分析时记下ProID,即可访问活动的详细信息分析数据同上,直接复制链接到浏览器,并粘贴到解析工具分析,结果如下发现返回是一个大字典,有三个键,我们要找的详细信息在Program键中,Program键的值也是一个字典,这个字典中:RegAgeMin表示最低年龄(int)RegAgeMax表示最大年龄(int)PositionsAvailable表示报名总人数(int)PositionsOccupied表示已报名人数(int)StartTime中含有活动开始时间(str)EndTime中含有活动结束时间(str)获得这些信息后,就可以运用if进行判断,筛选出符合条件的活动Python实现importimportrequests常量定义根据筛选条件,定义以下常量ACTIVITY_NUM=12#需要找到的符合条件的活动数量TARGET_TIME='2024-08-18'#目标日期AVAILABLE_POSITIONS=2#可招募人数AGE=15#年龄限制START_TIME=6#时间限制END_TIME=12#将开始时间设为6,结束时间设为12,代表上午PS=1000#请求返回的活动数(待会详细解释)变量定义p=0#页码activities=0#已检索活动数activity_links=[]#筛选出来的活动的页面链接初步筛选(筛出满足日期条件的活动)defdate():globalactivitiesurl=f"https://www.sva.org.cn/Default.aspx?_c=ProgramSearch&_a=GetPrograms&_p={p}&_ps={PS}"response=requests.get(url)programs=response.json().get("Programs")for_inprograms:activities+=1ifTARGET_TIMEin_.get('StartDate'):#因为StartDate内容为xxxx-xx-xxTxx:xx:xx所以要用in而不是==ifdetails(_.get('ProID')):returnTrue根据详细信息筛选(年龄,时间,人数等)defdetails(proid):url=f"https://www.sva.org.cn/Default.aspx?_c=Program&_a=GetProfile&rogramID={proid}"response=requests.get(url)program=response.json().get("Program")min_age,max_age=program.get("RegAgeMin"),program.get("RegAgeMax")if(max_ageandmin_age)isNone:min_age=0max_age=100elifmax_ageisNone:max_age=100available_positions=program.get("PositionsAvailable")-program.get("PositionsOccupied")start_time,end_time=int(program.get("StartTime")[11:13]),int(program.get("EndTime")[11:13])ifmin_age=AVAILABLE_POSITIONSandSTART_TIME=10000:ans=input('\n查询活动量已超10000,是否继续?(按enter继续,按a结束查询)')ifans=='a':breakifdate():breakprint(f'\n共检索{activities}个活动,其中,符合条件的活动链接为')foriinactivity_links:print(i)加了一个功能,在检索的活动大于10000个时,询问用户是否继续另外,可以选择importwebbrowser实现自动打开所有活动链接的功能小修小补有一些活动左上角标注了“排期”点进去后发现会有多个活动此时需要重新执行一遍初步筛选针对这种情况,我优化了一下程序不解释了,直接上程序importrequestsACTIVITY_NUM=10TARGET_TIME='2024-08-18'AVAILABLE_POSITIONS=2AGE=15START_TIME=6END_TIME=12PS=1000p=0activities=0activity_links=[]defdetails(proid):url=f'https://www.sva.org.cn/Default.aspx?_c=Program&_a=GetProfile&rogramID={proid}'response=requests.get(url)program=response.json().get("Program")min_age,max_age=program.get("RegAgeMin"),program.get("RegAgeMax")if(max_ageandmin_age)isNone:min_age=0max_age=100elifmax_ageisNone:max_age=100available_positions=program.get("PositionsAvailable")-program.get("PositionsOccupied")start_time,end_time=int(program.get("StartTime")[11:13]),int(program.get("EndTime")[11:13])ifmin_age=AVAILABLE_POSITIONSandSTART_TIME=10000:ans=input('\n查询活动量已超10000,是否继续?(按enter继续,按a结束查询)')ifans=='a':breakifsearch(f"https://www.sva.org.cn/Default.aspx?_c=ProgramSearch&_a=GetPrograms&_p={p}&_ps={PS}"):breakprint(f'\n共检索{activities}个活动,其中,符合条件的活动链接为')foriinactivity_links:print(i)selenium法其实最开始写这个程序我是用的selenium,主要是这个网站用了异步加载,如果不想去分析各种参数和URL,那用selenium是最省心的方法,不过实际操作下来用selenium的难度远超本文介绍的方法,而且难度高就算了,检索速度还慢,等待时间又久,甚至很容易报错,所以就尝试了一下老老实实的去分析,发现效果出奇的好,其他条件相同的情况下,request法得出结果的时间相比selenium缩短了约95%当然selenium肯定也有它的使用场景,比如一些查成绩的网站,header中带有加密后的数据,这种基本上就只会使用selenium了。不过在本项目中,因为志愿深圳并不复杂,也没有啥反爬措施(甚至可以不用伪造header直接爬),所以用request的方法结尾异步加载在现代网站上经常使用,但是一般不难分析,稍微花点时间就能找到请求URL,进而分析参数,返回数据。有些难爬的网站还要求伪造header,绕开反爬措施,这也是Python爬虫的有趣之处
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-10 05:49 , Processed in 0.736314 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表