2020年初的新冠疫情让全国人民没有过一个好的年,作为IT人,学习实验室安排了一个疫情查看程序的任务,鄙人的任务是写爬虫爬取数据。作为一个爬虫半吊子,经过两早上的自学,初步完成全国各省市数据以及总体数据的爬取。
我选择的是丁香园的网站:https://ncov.dxy.cn/ncovh5/view/pneumonia_peopleapp?from=timeline&isappinstalled=0
网页源代码巨丑无比,他的数据完全写死在了JS里面,这令人很无解,明明近在咫尺却又无法触碰。所以果断的放弃了beautiful的库,如果真解析出来的话官方稍微变一下JS的方式就前功尽弃了。这时把目标盯在渲染层elements上,发现数据在渲染层中似乎很舒服,有规律可循,也很好进行操作:
要获取这些信息,就需要用浏览器引擎去模拟渲染,最后再直接定位数据,套循环就都得到了,这应该是最简单的思路了吧,而且访问量也很小,只要请求一次就够了,剩下的都是解析,所以不会被认为是恶意访问。
首先是环境配置,开发环境当然是windows了,当然windows下环境配置也很容易,首先下载谷歌浏览器,然后再根据浏览器的版本去chromedriver官网下载chromedriver.exe,然后放到python的目录下边,这样免的配置环境变量。python直接使用pip工具下载selenium就可,推荐使用科学上网,这样会省很多时间。环境配置具体教程网上多的是。
环境配置好使用以下代码进行测试,如果有chrome浏览器打开丁香园网页且不报错,那说明环境配置成功。
from selenium import webdriver
driver = webdriver.Chrome()
url="https://ncov.dxy.cn/ncovh5/view/pneumonia_peopleapp?from=timeline&isappinstalled=0"
driver.get(url)
driver.close()
环境配置成功开始对Xpath进行定位,在谷歌的elements的每一行上右键按copy点开有copyXpath就可以获取该行元素的Xpath值。然后将所需要的数据全部Xpath搞下来,分析一下结构。
首先我们要获取数据更新时间,获得的他的Xpath为
//*[@id='root']/div/div[4]/div[3]/div/div[1]/span
他返回的是一长段文本数据,通过不同次的返回发现第3~19个字符为我们需要的内容,所以我们使用driver.find_element_by_xpath先把内容定位,再取text[3:19]即可获得数据。
以此类推,获取了很多的Xpath值。然后经过一个个的测试,遇到一个问题,省下面市的数据text给的是空值,???这不应该啊渲染出来了怎么还获取不到。仔细思考了一下,原来是隐藏的问题,需要先模拟点击省条目,让市的数据展开,然后才能获取啊,于是便找到了爬取每一个省的流程了。
省的数量很好确定是36,但是省里有多少市呢?这个根本无法得知,所以我想了这样一个办法,while里面套一个try-catch。
while(True):
try:
#somethings
catch Exception:
break;
这样就能很好解决问题了,当列表加载到最低端时就出异常了,然后知道他是怎么回事,直接跳出去就行了啊,所以省内市的数据也就很容易的解决了。
外面套一个大的for循环,循环36次,根据Xpath每次加一来获取各个省的数据,每次都要模拟点击省条目的箭头图标,让其展开,不至于获取空的数据。然后用上述的办法,while循环用来市的数据加1。就基本完事了。
但是还有一个运行之后才会发现的问题,香港澳门这些条目是省一级的,但是没有展开箭头图标,而且如果数据为0它就什么都不显示,所以完善的工作就开始了。没有箭头的继续使用惯用的办法try-catch,单独进行处理,不影响之后数据的处理,而返回为空的看他是空串还是null,单独写个判断处理一下变成0就可以了。
最后测试一遍,万事大吉,全部完成。然后后端那边找我要代码,后端是写在阿里云上的,所以真正起作用需要在linux上配置环境并运行。阿里云做善事,他们的镜像很全,不用科学上网就可以顺利配置环境,chrome用yum直接下载,selsiunm和chromedriver都可以用pip下载,超级容易。然后为了放置有错误需要修改代码,不显示浏览器,且各种配置都得搞好,不然第二句就会报错。
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--headless')
chrome_options.add_argument('blink-settings=imagesEnabled=false')
chrome_options.add_argument('--disable-gpu')
driver = webdriver.Chrome(chrome_options=chrome_options)
这样就可以了,然后记得在末尾加上下面的代码。
driver.close()
driver.quit()
不然进程关不掉很占服务器内存的,然后直接在java中写个调用shell脚本的语句,shell脚本是python XXX.py就可以了,里面写文件的路径记得要改,然后直接运行java后端代码就可以了。
我顺便附带点草稿,很丑,写文件部分的自己加吧。
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--headless')
chrome_options.add_argument('blink-settings=imagesEnabled=false')
chrome_options.add_argument('--disable-gpu')
driver = webdriver.Chrome(chrome_options=chrome_options)
pathStr_01 = "div[4]/div[3]"
pathStr_02 = "div[4]/div[11]"
print("start")
url="https://ncov.dxy.cn/ncovh5/view/pneumonia_peopleapp?from=timeline&isappinstalled=0"
driver.get(url)
path="XXX.JSON"#修改这里为你要的路径
f = open(path, mode='w',encoding='utf-8')
for i in range (3,37):
flag=True
flagNum=2
while(flag==True):
try:
city=driver.find_element_by_xpath("//*[@id='root']/div/"+pathStr_02+"/div[" + str(i) + "]/div[" + str(flagNum) + "]/p[1]/span")
print(city.text)
flagNum=flagNum+1
except Exception:
flag=False
break
f.close()
driver.close()
print("end")
我把所有写文件以及我的判断代码删掉了,因为我直接写的JSON文件,总体有点庞大,代码仅供借鉴。