制作从某网站中获得材料晶格参数的小爬虫总结

制作爬虫目的

由于师兄要求我查询十几个二维材料的所有禁带宽度、点阵常数,当我打开materialsProject网站,查找某个元素后,发现每个二维材料因为空间群的结构不同,也会有不同的禁带宽度和点阵常数。细数下如果要收集这些数据可能要打开一百多个网页,然后复制粘贴那些数值,为了解放机械性工作,所以凭借课余所学做了一个爬虫,来对这些数据抓取并打印下来。


了解网站,知道数据源文件的uri

首先打开该网站materialsproject,用“开发者工具进”行页面分析。搜索GaN,发现uri中包含了#符号,上网了解http不会请求#以后的数据,(关于http中带#号的介绍,可看https://blog.****.net/luka2008/article/details/38753269)。然后在分析中发现了一个uri是在点击“search”按钮后传回来的json文本,因此可以通过分析这个json文件的uri,来抓取相关数据,返回json文本的uri为

https://materialsproject.org/apps/materials_explorer/results?query=%7B%22reduced_cell_formula%22%3A%22GaN%22%7D   改其中红字上的内容,就可以获得想搜索的内容了

搜索结果的页面如下:

制作从某网站中获得材料晶格参数的小爬虫总结

图中红色框框里面的内容是我想获取的,因此解析json文件,将material_id和其值放到map对象的键值对中,再把每个map对象通过push()放到mapList数组中。

当点击上图的列表中的其中一行,会跳转到该id值材料的详细页面中,这时通过分析页面会发现也会收到一个json文件,是关于晶格常数的。其uri为通过上述方法,把所需数据放到数组中,当请求完网页后便可以将数据一一打印出来。

爬虫操作方法:通过node运行文件后,在控制台中输入所有想操作的分子式,以空格分开,回车后便运行,并且打印出自己想要的数据,成功避免一次流水线任务,haha...

实现原理:

  • 此爬虫使用nodejs实现的,用到了‘request-promise’模块以方便用async/await方法来等待收据接收完再放入数组并打印出来。
  • 在控制台输入的各个分子式在通过‘line’事件被监听后,会被放入到formulaArray数组中。然后通过遍历每个分子式,分别进行json文件请求
  • 在getValue()方法中,option对象的键值对是copy“开发者工具”分析页面时上的数据的,请求该uri,解析json文件,并把需要的数据放到map对象中,再把map对象填入mapList数组里面
  • 然后再通过访问数组对象中材料id获取第二个json文件,其中其晶格参数有ICSD(无机晶体学数据库)的也有computed的,这里优先选取ICSD的,若ICSD不存在,再选取后者的。
  • 最后通过遍历数组的对象,遍历对象的键值对,把其打印出来

爬虫代码

[javascript] view plain copy
  1. const rp=require('request-promise');  
  2. let mapList=[];//存放map对象的数组  
  3.   
  4. const readLine=require('readline');  
  5. const rl=readLine.createInterface({  
  6.     input:process.stdin,  
  7.     output:process.stdout  
  8. });  
  9. rl.on('line',async function(line){  
  10.     let formulaArray=[];//存放分子式的数组  
  11.     if (line.trim()) {  
  12.         formulaArray=line.split(' ');  
  13.         for(let index=0;index<formulaArray.length;index++){//遍历每个分子式  
  14.             await getValue(formulaArray[index]);//请求网页并存储map于mapList数组中  
  15.         }  
  16.         console.log('打印结束');  
  17.     }  
  18. });  
  19. rl.on('close'function () {  
  20.     console.log('程序结束');  
  21.     process.exit(0);  
  22. });  
  23.   
  24. async function getValue(formula){  
  25.     let options = {  
  26.         uri: 'https://materialsproject.org/apps/materials_explorer/results?query=%7B%22reduced_cell_formula%22%3A%22'+formula+'%22%7D',  
  27.         method: 'GET',  
  28.         headers: {  
  29.             'Accept''application/json, text/javascript, */*; q=0.01',  
  30.             'Accept-Language''zh-CN,zh;q=0.9',  
  31.             'Connection''keep-alive',  
  32.             'Content-Type''application/x-www-form-urlencoded; charset=UTF-8',  
  33.             'Host''materialsproject.org',  
  34.             'Referer''https://materialsproject.org/',  
  35.             'User-Agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'  
  36.         },  
  37.         json:true//此行会自动把传回来的数据进行json解析  
  38.     };  
  39.     await rp(options)//获取第一个json文件中的id、分子式、空间群符号和分子的禁带宽度  
  40.         .then(function(res){  
  41.             for(let i=0;i<res.length;i++){  
  42.                 let map=new Map();  
  43.                 map.set('materials_id',res[i].materials_id);  
  44.                 map.set('formula',res[i].formula);  
  45.                 map.set('symbol',res[i].spacegroup.symbol);  
  46.                 map.set('bandgap',res[i]['band_gap (eV)']);  
  47.                 mapList.push(map);  
  48.                 //console.log(i);  
  49.             }  
  50.         })  
  51.         .catch(function(err){  
  52.         console.log(err);  
  53.     });  
  54.     await getLatticeValue();//获取第二个json文件中的点阵常数数据  
  55.     for(let i=0;i<mapList.length;i++){//打印数据  
  56.         let strLine='';  
  57.         mapList[i].forEach(function (item) {  
  58.             strLine=strLine+' , '+item.toString();  
  59.         });  
  60.         console.log(strLine);  
  61.     }  
  62.     mapList=[];  
  63. }  
  64.   
  65. async function getLatticeValue(){  
  66.     let optionD = {  
  67.         uri: '',  
  68.         method: 'GET',  
  69.         headers: {  
  70.             'Accept''*/*',  
  71.             'Accept-Language''zh-CN,zh;q=0.9',  
  72.             'Connection''keep-alive',  
  73.             'Content-Type''application/x-www-form-urlencoded; charset=UTF-8',  
  74.             'Host''materialsproject.org',  
  75.             'Referer''https://materialsproject.org/materials/mp-1434/',  
  76.             'User-Agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'  
  77.         },  
  78.         json:true  
  79.     };  
  80.     for(let i=0;i<mapList.length;i++){  
  81.         optionD.uri='https://materialsproject.org/materials/'+mapList[i].get("materials_id")+'/structure';  
  82.         //console.log(optionD.uri);  
  83.         await rp(optionD)  
  84.             .then(function (res) {  
  85.                 if(res.exp_lattice!==null){  
  86.                     mapList[i].set('latticeFrom','ICSD');  
  87.                     mapList[i].set('a',res.exp_lattice.a);  
  88.                     mapList[i].set('b',res.exp_lattice.b);  
  89.                     mapList[i].set('c',res.exp_lattice.c);  
  90.                     mapList[i].set('alpha',res.exp_lattice.alpha);  
  91.                     mapList[i].set('beta',res.exp_lattice.beta);  
  92.                     mapList[i].set('gamma',res.exp_lattice.gamma);  
  93.                 }else {  
  94.                     mapList[i].set('latticeFrom','computed');  
  95.                     mapList[i].set('a',res.structure.lattice.a);  
  96.                     mapList[i].set('b',res.structure.lattice.b);  
  97.                     mapList[i].set('c',res.structure.lattice.c);  
  98.                     mapList[i].set('alpha',res.structure.lattice.alpha);  
  99.                     mapList[i].set('beta',res.structure.lattice.beta);  
  100.                     mapList[i].set('gamma',res.structure.lattice.gamma);  
  101.                 }  
  102.             }).catch(function (err) {  
  103.                 console.log(err);  
  104.             });  
  105.     }  
  106. }  

运行结果

如下所示,数据从左到右依次为:材料id、分子式、空间群符号、禁带宽度、数据来源,以及点阵常数的a、b、c、alpha、beta、gamma。

制作从某网站中获得材料晶格参数的小爬虫总结

程序运行正常,打印所有分子式后把数据粘贴到excel交付给师兄就好了。。。


实验中遇到的问题以及解决方法

  • Q1:请求uri后,获取的内容为乱码?
  • A1:不让代码被压缩,在option中因为添加了此句:Accept-Encoding: gzip, deflate, br,则响应的内容就会被压缩,而在内容处理中又没有引用解码的模块,所以会乱码,因此删除该句便可
  • Q2:json中key含有空格,该如何处理?
  • A2:在请求到的第一个json文件中,需要获得value的禁带宽度,其key含有空格,在访问时这样写['key']便可以,具体看这里