实验原因:

目前有一个医疗百科检索项目,该项目中对关键词进行检索后,返回的结果很多,可惜结果的排序很不好,影响用户体验。简单来说,搜索出来的所有符合疾病中,有可能是最不常见的疾病是排在第一个的,而最有可能的疾病可能需要翻很多页才能找到。

实验目的:

为了优化对搜索结果的排序,想到了利用百度搜索后有显示搜索到多少词条,利用这个词条数,可以有效的对疾病排名进行一个优化。从一方面看,某一个疾病在百度的搜索词条数目越多,表示这个词条的信息特别丰富,侧面反映了搜索这个词条的人特别多,从而可以推出这个疾病在人群中可能是发生概率较高的。反过来看,如果一个疾病很罕见,人们只有很低的概率会患这种疾病,那么相应的搜索这个词条的人也就会少,相应的网页也就少,因此搜索引擎搜索出来的词条数目也就会少。

实验过程:

第一阶段:从数据库中获取疾病名称

  这一阶段涉及到如何利用python从数据库中提取数据,我利用的是MySQLdb库,通过利用以下代码建立连接提取数据:

db = MySQLdb.connect('localhost', 'root', '', 'medical_app', charset = 'utf8')
cu = db.cursor()
cu.execute('select * from table order by id')

  其中在connect函数中我设置了charset属性,这是因为如不这样做python读取出来的数据库中文信息会变成乱码。

  在这一阶段,我编写了一个dbmanager类,主要负责数据库的读取和插入工作,刚才介绍的已经可以完成一个读取任务了,那么怎么利用python对数据进行添加呢?

cu.execute('insert into table(id,name) value(?, ?)',[a,b])

第二阶段:完成数据的爬取

  最初我尝试了利用python的urllib来对百度网页进行爬取,发现这条路是走不通的,百度发现是机器爬取后会返回错误的网页代码。

  于是就想到了模拟浏览器的方式来对百度网页进行互动,爬取网页的内容。找到了mechanize库,表示这个库非常好用,使用非常简单。除了能够模仿浏览器读取网页以外,还可以和网页进行交互操作,然后还可以设置机器人选项,通过这个选项可以读取屏蔽机器人的网页,比如说百度。

br = mechanize.Browser()
br.open("http://www.example.com/")
# follow second link with element text matching regular expression
response1 = br.follow_link(text_regex=r"cheese\s*shop", nr=1)
assert br.viewing_html()
print br.title()
print response1.geturl()
print response1.info() # headers
print response1.read() # body br.select_form(name="order")
# Browser passes through unknown attributes (including methods)
# to the selected HTMLForm.
br["cheeses"] = ["mozzarella", "caerphilly"] # (the method here is __setitem__)
# Submit current form. Browser calls .close() on the current response on
# navigation, so this closes response1
response2 = br.submit()

以上是简单的mechanize的使用说明。以下是官网对mechanize的简单说明,不得不说,这个配合HTML解析器用起来太方便了。

  • mechanize.Browser and mechanize.UserAgentBase implement the interface of urllib2.OpenerDirector, so:

    • any URL can be opened, not just http:

    • mechanize.UserAgentBase offers easy dynamic configuration of user-agent features like protocol, cookie, redirection and robots.txt handling, without having to make a new OpenerDirector each time, e.g. by callingbuild_opener().

  • Easy HTML form filling.

  • Convenient link parsing and following.

  • Browser history (.back() and .reload() methods).

  • The Referer HTTP header is added properly (optional).

  • Automatic observance of robots.txt.

  • Automatic handling of HTTP-Equiv and Refresh.

  关于BeautifulSoup,这是一个在数据挖掘领域非常好用的HTML解析器,他将网页解析成有标签所构成的树形字典结构,想要找到网页中的某一元素用find函数非常轻松的就可以找到。

  

html = urllib.open('http://mypage.com')
soup = BeautifulSoup(html.read())
soup.find('div', {'class':'nums'})

  上面这句话的意思是,找到网页中class属性为nums的div标签内容。

第三阶段:异常的捕获和超时设置

  爬取网页内容的爬虫已经写好,拿出来跑一跑,发现百度经常性的会返回一些错误的网页导致网页无法正确填充表格或者分析,这个时候我们需要抓取这里面的异常让程序能够持续运行,让抓取出现异常的疾病名称放回队列稍后再进行爬取。关于异常捕获,代码如下:

  

 try:
br = mechanize.Browser()
br.set_handle_robots(False)
br.open(URL)
br.select_form('f')
br['wd'] = name[1].encode('utf8')
response = br.submit()
#print 'form submitted, waiting result...'
#分析网页,有可能百度返回错误页面
soup = BeautifulSoup(response.read())
# text = soup.find('div', {'class':'nums'}).getText()
if soup.find('div', {'class':'nums'}):
text = soup.find('div', {'class':'nums'}).getText()
else:
print '$Return page error,collect again...'
self.manual.push_record(name)
continue
except socket.timeout:
print '$there is an error occur.it will check later...'
self.manual.push_record(name)
print name[1],' pushed into the list.'
continue

  这里可以看到为了提高检索效率,设置了一个超时异常,利用socket组件中的timeout。在这段代码之前我们需要设置下超时时间

 import socket

 socket.setdefaulttimeout(5)

 try:
...
except socket.timeout :
print 'timeout'

  这段示例代码设置的是5秒钟的超时时间。

  

  到目前为止,利用这些知识,我已经完成了一个在爬取过程中不会出错的一个单线程爬虫模块,显然这个爬虫爬取内容的效率是非常慢的。我决定用多线程来让它快起来。

第四阶段:多线程的爬虫控制

  这一阶段,我们需要设计一个可以进行多线程网页爬取的爬虫设计。这里面我们主要考虑两点:1,如何实现多线程;2,关于公共变量的同步读取问题如何实现。

  对于第一个问题,在python中多线程的实现有两种方式:

    第一种是函数式:

函数式:调用thread模块中的start_new_thread()函数来产生新线程。语法如下:

thread.start_new_thread ( function, args[, kwargs] )

参数说明:

  • function - 线程函数。
  • args - 传递给线程函数的参数,他必须是个tuple类型。
  • kwargs - 可选参数。

   第二种是线程模块:

Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。

thread 模块提供的其他方法:

  • threading.currentThread(): 返回当前的线程变量。
  • threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:

  • run(): 用以表示线程活动的方法。
  • start():启动线程活动。
  • join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
  • isAlive(): 返回线程是否活动的。
  • getName(): 返回线程名。
  • setName(): 设置线程名。

  之前尝试了利用第一种方式来实现函数,发现这样实现出来的代码结构很不清晰,在变量同步的时候回会显得非常混乱。于是采用了第二种方法来实现这个函数:

class myThread (threading.Thread):   #继承父类threading.Thread

    def __init__(self, threadID, name, Manual):
threading.Thread.__init__(self)
self.lock = thread.allocate_lock()
self.threadID = threadID
self.name = name
self.manual = Manual def run(self): #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
print "Starting " + self.name
self.get_rank()
print "Exiting " + self.name def get_rank(self): #爬虫代码,持续获取疾病得分
...
...

  如何实现线程的运作呢?代码如下:

for i in xrange(thread_count): #建立线程
mythread = myThread(i,'thread-'+str(i),m)
thread_queue.append(mythread)
for i in xrange(thread_count): #启动线程
thread_queue[i].start()
for i in xrange(thread_count): #结束线程
thread_queue[i].join()

  现在我们已经可以通过利用线程来对疾病进行爬取了,可是在对爬取结果怎么进行同步存储,怎么对疾病名称进行同步读取呢?

第四阶段:变量的同步操作

  这一阶段我们需要设计好如何才能够可行的对同步变量进行操作,这一方面是比较烧脑的。。。设计如下:

  

  其中B类是线程类,A类是同步变量控制类,A类的主要功能是提供变量V1,V2的同步操作,包括读取写入之类的。B类是线程类,负责爬取网页的数据。

  B类在上面已经说过了,A类的实现如下:

class Manual: #同步变量控制

    def __init__(self, names):
self.names = names
self.results = []
self.lock = threading.RLock() def get_name(self): #获得疾病名称
self.lock.acquire()
if len(self.names):
name = self.names.pop()
#print 'name get'
self.lock.release()
return name
else:
self.lock.release()
return None def put_result(self, result): #存放得分
self.lock.acquire()
self.results.append(result)
print '(%d/6811)' %len(self.results)
self.lock.release() def push_record(self, name): #放回获取失败的疾病名
self.lock.acquire()
self.names.append(name)
self.lock.release()

  最后,所有部分都已实现,就差组装起来跑动啦。目前在实验室苦逼的跑啊跑中。。网不给力,拿了4个线程跑,保守目测得4个小时。

【原创-Blaxon】

利用python爬取海量疾病名称百度搜索词条目数的爬虫实现的更多相关文章

  1. 利用python爬取城市公交站点

    利用python爬取城市公交站点 页面分析 https://guiyang.8684.cn/line1 爬虫 我们利用requests请求,利用BeautifulSoup来解析,获取我们的站点数据.得 ...

  2. 利用python爬取58同城简历数据

    利用python爬取58同城简历数据 利用python爬取58同城简历数据 最近接到一个工作,需要获取58同城上面的简历信息(http://gz.58.com/qzyewu/).最开始想到是用pyth ...

  3. 没有内涵段子可以刷了,利用Python爬取段友之家贴吧图片和小视频(含源码)

    由于最新的视频整顿风波,内涵段子APP被迫关闭,广大段友无家可归,但是最近发现了一个"段友"的app,版本更新也挺快,正在号召广大段友回家,如下图,有兴趣的可以下载看看(ps:我不 ...

  4. 利用Python爬取豆瓣电影

    目标:使用Python爬取豆瓣电影并保存MongoDB数据库中 我们先来看一下通过浏览器的方式来筛选某些特定的电影: 我们把URL来复制出来分析分析: https://movie.douban.com ...

  5. 利用Python爬取朋友圈数据,爬到你开始怀疑人生

    人生最难的事是自我认知,用Python爬取朋友圈数据,让我们重新审视自己,审视我们周围的圈子. 文:朱元禄(@数据分析-jacky) 哲学的两大问题:1.我是谁?2.我们从哪里来? 本文 jacky试 ...

  6. 利用python爬取贝壳网租房信息

    最近准备换房子,在网站上寻找各种房源信息,看得眼花缭乱,于是想着能否将基本信息汇总起来便于查找,便用python将基本信息爬下来放到excel,这样一来就容易搜索了. 1. 利用lxml中的xpath ...

  7. 利用Python爬取电影网站

    #!/usr/bin/env python #coding = utf-8 ''' 本爬虫是用来爬取6V电影网站上的电影资源的一个小脚本程序,爬取到的电影链接会通过网页的形式显示出来 ''' impo ...

  8. 如何利用python爬取网易新闻

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: LSGOGroup PS:如有需要Python学习资料的小伙伴可以 ...

  9. 利用Python爬取可用的代理IP

    前言 就以最近发现的一个免费代理IP网站为例:http://www.xicidaili.com/nn/.在使用的时候发现很多IP都用不了. 所以用Python写了个脚本,该脚本可以把能用的代理IP检测 ...

随机推荐

  1. 《JAVA与模式》之组合模式

    定义(GoF<设计模式>): 将对象组合成树形结构以表示“部分整体”的层次结构.组合模式使得用户对单个对象和使用具有一致性. 及角色: 1.Component 是组合中的对象声明接口,在适 ...

  2. Verilog学习笔记简单功能实现(一)...............D触发器

    module D_flop(data,clk,clr,q,qb); input data,clk,clr; output q,qb; wire a,b,c,d,e,f,ndata,nclk; nand ...

  3. 安装多JDK后,java编译环境和运行环境版本(JDK版本) 不一致解决:

    由于之前安装过JDK1.7 ,现在一个项目是JDK1.5的,那么需要更改了环境变量了,此处不再赘述如何设置JDK 的环境变量了.然后网上找来方法: 在安装多个jdk后,出现了java -version ...

  4. How to Install Hadoop on Ubuntu

    安装教程,https://www.digitalocean.com/community/tutorials/how-to-install-hadoop-on-ubuntu-13-10

  5. ubuntu vps 安装java

    Introduction Java is a programming technology originally developed by Sun Microsystems and later acq ...

  6. MVC.Net: 解决Attempted to access an unloaded appdomain的问题

    在C#中尝试获取AD帐号信息时,会随机出现Attempted to access an unloaded appdomain的问题,解决方法如下: 将 principalContext = new P ...

  7. File存储 - 文件存储

    博客地址 http://www.cnblogs.com/mmyblogs/p/6107472.html(转载请保留) 文件存储 文件存储是 Android 中最基本的一种数据存储方式,它不对存储的内容 ...

  8. Uva 110 - Meta-Loopless Sorts(!循环,回溯!)

    题目来源:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=3&pa ...

  9. Git+GitHub 使用小结

    1.Git安装完成后需要做的配置            $ git config --global user.name "Your Name"        $ git confi ...

  10. 超酷的测速网站Ookla SPEEDTEST

    测试网速的工具.网站估计不少,在百度一搜都能搜出一大堆,下面介绍一个国外测试网速的网站,用户体验相当棒,感觉酷毙了,那些其它测试网速的网站跟这个比起来,简直弱毙了.这个网速测试网站就是:http:// ...