用Python实现一个爬虫爬取ZINC网站进行生物信息学数据分析

  最近接到实验室的导师交给我的一个任务,就是他们手头有很多smile表达式,格式类似这种:C(=C(c1ccccc1)c1ccccc1)c1ccccc1(这是生物信息学中表达小分子结构的一种常用表达式),他们需要对每个smile表达式在ZINC网站(生物信息学数据网站)上进行搜索,然后找到对应的ZINC号、小分子供应商、构象预测等信息。基本步骤如下:

点击查找之后网页就会跳转到详细信息,我们需要获取它的ZINC号、小分子供应商、构象预测、CAS号等信息,如下:

  这一套流程要是靠人工手动完成的话有点不太现实,毕竟他们有一千多个这样的smile号,要是一个一个搞还不要累死,于是他们想到了我,想让我写一个爬虫来自动化提取出这些信息,一劳永逸。说实话我当时接到这个任务的时候毫不犹豫答应了下来,一来是我之前确实写过类似的程序,二来自己最近确实很闲,毕业论文搞得差不多了,每天没啥事打打王者一天的时间就浪费掉了,还不如接个任务,也算是练练手。废话不多说,直接开干。

  在撸代码之前我们要先搞清楚几个问题,不能蛮干。首先我们要知道在我们输入smile号并点击搜索的时候,这个时候前端和后端服务器交互的过程是什么样的,也就是说前端到底给后端发送了什么样的HTTP请求。要知道我们一开始可是只输入了一个smile号,网页就直接跳转到了http://zinc15.docking.org/substances/ZINC000001758809/,这肯定是后台根据smile号查出ZINC号之后回应了一个重定向请求,猜想是这样,我们来看看实际情况。在浏览器中右键点击检查,查看在我们操作的过程中浏览器到底向后台发送了哪些请求。

从上图可以看到,我们一旦键入smile表达式之后,浏览器立马给后台发送了一个请求,然后网页显示出一个小分子的图像,很显然这个请求是为了获取小分子构象信息然后生成图片的,这个流程我们不做深究,我们要知道到底发送什么请求才能获得重定向后的地址,并拿到真正有用的网页。我们点击搜索,接着往下看:

在接下来的请求中,我们发现了一个关键请求(上图标红处),这个请求的响应体返回的是一个序列号,如下图:

不要小看这个序列号,虽然我也不知道它具体代表什么意思,但是后面的请求充分向我们说明了这个序列号的重要性,即后面需要smile表达式带上这个序列号一起发送一个HTTP请求,才能获取到那个关键的重定向网页,如下图:

  到目前为止,这个网页的请求逻辑已经很清楚了,我们只需要利用python模仿浏览器发送同样的请求,首先获取这个inchikey序列号,然后通过这个序列号和smile表达式再次发起请求就能得到重定向的网址了,然后对这个重定向网址发起请求就能获得我们所需要的关键网页了,我们所需要的全部信息都包含在这个重定向后的网页里,然后只要解析这个html网页,从中提取出我们想要的信息就行了。思路已经很清晰了,可以撸代码了,具体Python代码如下:

  1 #coding=utf-8
  2
  3 ‘‘‘
  4 @Author: [email protected]
  5 @Date: 2019-6-1
  6 @Description:
  7 本爬虫运行环境为python2.7,在python3中不能运行。运行前先将含有smile表达式的文件命名为SMILE.txt放在与本文件相同的目录下,执行程序后,
  8 本爬虫会自动读取SMILE.txt文件中的内容,并根据smile表达式抓取ZINC网站的网页进行分析,得到的结果会以当前时间命名放在当前执行目录下。
  9 PS:程序运行快慢取决于当前网速和SMILE.txt文件大小,请耐心等待。
 10 ‘‘‘
 11
 12 import os,sys
 13 import urllib
 14 import urllib2
 15 import json
 16 import time
 17 import re
 18 from HTMLParser import HTMLParser
 19 from datetime import datetime
 20
 21 headers = {
 22             "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
 23             "Accept-Encoding": "gzip, deflate",
 24             "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
 25             "Host": "zinc15.docking.org",
 26             "Referer": "http://zinc15.docking.org/substances/home/",
 27             "Upgrade-Insecure-Requests": "1",
 28             "Cookie": "_ga=GA1.2.1842270709.1559278006; _gid=GA1.2.1095204289.1559278006; _gat=1; session=.eJw9zLEKgzAQANBfKTd3qcRFcEgJBIdLQE7hbhFqW6pRC20hGPHf26nvA94G3XCFYoPDBQoQCjlm7YCzTDLWk6N2xBSi2CoKcXSzjGJ0zqkvYT9C_37du88z3JZ_gXP98MTJWY6eesXUKG85RwonTs3q6BzEyOQMrmirzCWtUJe_bv8CllwtkQ.D9Kh8w.M2p5DfE-_En2mAGby_xvS01rLiU",
 29             "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
 30             }
 31
 32 #解析SEA Predictions
 33 def getSeaPredictions(html):
 34     seaPrediction = []
 35     begin = html.find("SEA Predictions", 0)
 36     if(begin == -1):
 37         return seaPrediction
 38     end = html.find("</tbody>", begin)
 39     left = html.find("<td>", begin)
 40     right = html.find("</td>", left)
 41     pattern = re.compile(‘>(.*?)<‘)
 42     while(right < end):
 43         str = html[left:right+5]
 44         str = str.replace("\n", "").replace("\t", "")
 45         str = ‘‘.join(pattern.findall(str))
 46         str = str.strip()
 47         seaPrediction.append(‘ ‘.join(str.split()))
 48         left = html.find("<td>", right)
 49         right = html.find("</td>", left)
 50         if(left == -1 or right == -1):
 51             break
 52     return seaPrediction
 53
 54 #解析Vendors
 55 def getVendors(zincNum):
 56     url = "http://zinc15.docking.org/substances/" + zincNum + "/catitems/subsets/for-sale/table.html"
 57     request = urllib2.Request(url, headers = headers)
 58     response = urllib2.urlopen(request)
 59     html = response.read()
 60
 61     #获取More about字段结束的位置列表,在其附近查找Vendors
 62     indexList = []
 63     begin = 0
 64     index = html.find("More about ", begin)
 65     while(index != -1):
 66         indexList.append(index + 11)
 67         begin = index + 11
 68         index = html.find("More about ", begin)
 69
 70     vendors = []
 71     pattern = re.compile(‘>(.*?)<‘)
 72     for i in range(len(indexList)):
 73         begin = indexList[i]
 74         end = html.find(‘">‘, begin)
 75         vendors.append(html[begin:end])
 76
 77         begin = html.find("<td>", end)
 78         end = html.find("</td>", begin)
 79         str = html[begin:end+5]
 80         vendors.append(‘‘.join(pattern.findall(str)))
 81
 82     return vendors
 83
 84 #解析CAS numbers
 85 def getCasNum(html):
 86     result = re.search("<dt>CAS Numbers</dt>", html)
 87     if(result == None):
 88         return "None"
 89     begin = result.span()[1]
 90     begin = html.find("<dd>", begin, len(html))
 91     begin = begin + 4
 92     end = html.find("</dd>", begin, len(html))
 93     if(begin + 1 >= end):
 94         return "None"
 95     str = html[begin:end]
 96     casNumList = re.findall(‘[0-9]+-[0-9]+-[0-9]+‘, str)
 97     if(casNumList == None):
 98         return "None"
 99     casNumStr = ""
100     for i in range(len(casNumList)):
101         casNumStr = casNumStr + casNumList[i]
102         if(i != len(casNumList)-1):
103             casNumStr = casNumStr + ","
104     return casNumStr
105
106 #解析ZINC号
107 def getZincNum(html):
108     result = re.search("Permalink", html)
109     if result is None:
110         return None
111     else:
112         begin = result.span()[1]
113         while(html[begin] != ‘\n‘):
114             begin = begin +1
115         begin = begin + 1
116         end = begin
117         while(html[end] != ‘\n‘):
118             end = end + 1
119         zincNum = html[begin:end]
120         return zincNum.strip()
121
122 #解析网页数据并写入文件
123 def parseHtmlAndWriteToFile(smile, html, output):
124
125     zincNum = getZincNum(html)
126     if zincNum is None:
127         print "ZINC number:\tNone"
128         output.write("ZINC number:\tNone\n")
129         return
130     else:
131         print "ZINC number: " + zincNum
132         output.write("ZINC number:\t" + zincNum + ‘\n‘)
133
134     casNum = getCasNum(html)
135     print "CAS numbers: " + casNum
136     output.write("CAS numbers:\t" + casNum + ‘\n‘)
137
138     output.write(‘\n‘)
139
140     vendors = getVendors(zincNum)
141     if(0 == len(vendors)):
142         print "Vendors:\tNone"
143         output.write("Vendors:\tNone\n")
144     else:
145         print "Vendors:\t"+str(len(vendors)/2)+" total"
146         output.write("Vendors:\t"+str(len(vendors)/2)+" total\n")
147         i = 0
148         while(i < len(vendors)-1):
149             output.write(vendors[i]+" | "+vendors[i+1]+"\n")
150             i = i + 2
151
152     output.write(‘\n‘)
153
154     seaPrediction = getSeaPredictions(html)
155     if(0 == len(seaPrediction)):
156         print "SEA Prediction:\tNone"
157         output.write("SEA Prediction:\tNone\n")
158     else:
159         print "SEA Prediction:\t"+str(len(seaPrediction)/5)+" total"
160         output.write("SEA Prediction:\t"+str(len(seaPrediction)/5)+" total\n")
161         i = 0
162         while(i < len(seaPrediction)-4):
163             output.write(seaPrediction[i] + " | " + seaPrediction[i+1] + " | " + seaPrediction[i+2] + " | " + seaPrediction[i+3] + " | " + seaPrediction[i+4] +"\n")
164             i = i + 5
165
166 #向重定向地址发起请求,获取网页数据
167 def getPage(url):
168     request = urllib2.Request(url, headers = headers)
169     response = urllib2.urlopen(request)
170     html = response.read()
171     return html
172
173
174 #构造url发起请求,获取inchikey然后获取重定向地址
175 def getRedirectUrl(smile):
176     encodeSmile = urllib.quote(smile, ‘utf-8‘)
177     url = ‘http://zinc15.docking.org/apps/mol/convert?from=‘ + encodeSmile + ‘&to=inchikey&onfail=error‘
178     request = urllib2.Request(url, headers = headers)
179     response = urllib2.urlopen(request)
180     inchikey = response.read()
181
182     url = ‘http://zinc15.docking.org/substances/?highlight=‘ + encodeSmile + ‘&inchikey=‘ + inchikey + ‘&onperfect=redirect‘
183     request = urllib2.Request(url, headers = headers)
184     newUrl = urllib2.urlopen(request).geturl()
185     return newUrl
186
187
188 def main():
189     inputFilename = "D:\python\SMILE.txt"
190     #outputFilename = datetime.now().strftime(‘%Y-%m-%d-%H-%M-%S‘) + ".txt"
191     outputFilename = "result.txt"
192     with open(inputFilename, "r") as input, open(outputFilename, "w") as output:
193         for line in input.readlines():
194             smile = line.strip()
195             print "SMILE:\t" + smile
196             output.write("SMILE:\t" + smile + ‘\n‘)
197             newUrl = getRedirectUrl(smile)
198             print newUrl
199             html = getPage(newUrl)
200             parseHtmlAndWriteToFile(smile, html, output)
201             print ‘\n‘
202             output.write(‘\n\n\n‘)
203
204 if __name__ == "__main__":
205     main()

以下是程序运行时截图:

最终抓取到的文本信息截图:

完美解决问题!!!

这里总结一下写爬虫需要注意哪些问题:

1、要摸清楚网站前后端交互的逻辑,要明确知道你的爬虫需要哪些网页,哪些网页是包含关键信息的网页,我们应该怎样构造请求获取它们。逻辑清晰了,思路就有了,代码写起来就快了。

2、解析html文件的时候思路灵活一点,各种正则表达式和查询过滤操作可以混着来,解析html文件归根到底还是对于字符串的处理,尤其是不规范的html文件,更能考验编程功底。

以上,可能是我大学在校期间做的最后一个项目了,四年的大学生活即将结束,感慨颇多。毕业之后即将走上工作岗位,心里既有一份期待也有一些焦虑,程序员这条道路我走得并不容易,希望以后一切顺利,与诸君共勉!

原文地址:https://www.cnblogs.com/jeysin/p/10962316.html

时间: 2024-10-28 18:13:34

用Python实现一个爬虫爬取ZINC网站进行生物信息学数据分析的相关文章

Python编写网页爬虫爬取oj上的代码信息

OJ升级,代码可能会丢失. 所以要事先备份. 一开始傻傻的复制粘贴, 后来实在不能忍, 得益于大潇的启发和聪神的原始代码, 网页爬虫走起! 已经有段时间没看Python, 这次网页爬虫的原始代码是 python2.7版本, 试了一下修改到3.0版本, 要做很多包的更替,感觉比较烦,所以索性就在这个2.7版本上完善了. 首先观赏一下原始代码,我给加了一些注释: # -*- coding: cp936 -*- import urllib2 import urllib import re import

python爬虫--爬取某网站电影下载地址

前言:因为自己还是python世界的一名小学生,还有很多路要走,所以本文以目的为向导,达到目的即可,对于那些我自己都没弄懂的原理,不做去做过多解释,以免误人子弟,大家可以网上搜索. 友情提示:本代码用到的网址仅供交流学习使用,如有不妥,请联系删除. 背景:自己有台电脑要给老爸用,老爷子喜欢看一些大片,但是家里网络环境不好,就想批量下载一些存到电脑里.但是目前大部分的网站都是这样的, 需要一个个地点进去,才能看到下载地址 如果我要下载100部电影,那肯定手都要点断了,于是便想把这些地址给爬取出来,

python 爬虫爬取 证券之星网站

周末无聊,找点乐子... #coding:utf-8 import requests from bs4 import BeautifulSoup import random import time #抓取所需内容 user_agent = ["Mozilla/5.0 (Windows NT 10.0; WOW64)", 'Mozilla/5.0 (Windows NT 6.3; WOW64)',               'Mozilla/5.0 (Windows NT 6.1) A

python beautifulsoup bs4爬虫 爬取糗事百科

声明:仅用于学习语法,请勿用于非法用途 import urllib.request import re from bs4 import BeautifulSoup # -*- coding:utf-8 -*- url = 'http://www.qiushibaike.com/hot/' user_agent='Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' headers={'User-Agent':user_agent} request = u

用Python爬虫爬取广州大学教务系统的成绩(内网访问)

用Python爬虫爬取广州大学教务系统的成绩(内网访问) 在进行爬取前,首先要了解: 1.什么是CSS选择器? 每一条css样式定义由两部分组成,形式如下: [code] 选择器{样式} [/code] 在{}之前的部分就是"选择器"."选择器"指明了{}中的"样式"的作用对象,也就是"样式"作用于网页中的哪些元素.可参考:http://www.w3school.com.cn/cssref/css_selectors.asph

如何利用Python网络爬虫爬取微信朋友圈动态--附代码(下)

前天给大家分享了如何利用Python网络爬虫爬取微信朋友圈数据的上篇(理论篇),今天给大家分享一下代码实现(实战篇),接着上篇往下继续深入. 一.代码实现 1.修改Scrapy项目中的items.py文件.我们需要获取的数据是朋友圈和发布日期,因此在这里定义好日期和动态两个属性,如下图所示. 2.修改实现爬虫逻辑的主文件moment.py,首先要导入模块,尤其是要主要将items.py中的WeixinMomentItem类导入进来,这点要特别小心别被遗漏了.之后修改start_requests方

Python爬虫爬取数据的步骤

爬虫: 网络爬虫是捜索引擎抓取系统(Baidu.Google等)的重要组成部分.主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份. 步骤: 第一步:获取网页链接 1.观察需要爬取的多网页的变化规律,基本上都是只有小部分有所变化,如:有的网页只有网址最后的数字在变化,则这种就可以通过变化数字将多个网页链接获取: 2.把获取得到的多个网页链接存入字典,充当一个临时数据库,在需要用时直接通过函数调用即可获得: 3.需要注意的是我们的爬取并不是随便什么网址都可以爬的,我们需要遵守我们的

python爬虫—爬取英文名以及正则表达式的介绍

python爬虫—爬取英文名以及正则表达式的介绍 爬取英文名: 一.  爬虫模块详细设计 (1)整体思路 对于本次爬取英文名数据的爬虫实现,我的思路是先将A-Z所有英文名的连接爬取出来,保存在一个csv文件中:再读取csv文件当中的每个英文名链接,采用循环的方法读取每一个英文名链接,根据每个英文名链接爬取每个链接中的数据,保存在新的csv文件当中. 需要写一个爬取英文名链接的函数.将爬取的内容保存在csv文件的函数以及读取csv文件内容的函数.爬取英文名详情页内容的函数. 表5.3.1 函数名

python爬虫爬取微博评论案例详解

这篇文章主要介绍了python爬虫爬取微博评论,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 数据格式:{"name":评论人姓名,"comment_time":评论时间,"comment_info":评论内容,"comment_url":评论人的主页} 以上就是我们需要的信息. 具体操作流程: 我们首相将主页获取完成以后,我们就会发现,其中 的内容带有相