包含python爬取函数实现的词条

python爬虫---爬取LOL云顶之弈数据本来是想爬取之后作最佳羁绊组合推算,但是遇到知识点无法消化(知识图谱),所以暂时先不组合了,实力有限
库的安装
1.requests#爬取棋子数据
2.json#棋子数据为js动态,需使用json解析
3.BeautifulSoup
实战前先新建个lol文件夹作为工作目录 , 并创建子目录data , 用于存放数据 。
1.爬取数据,新建个py文件,用于爬取云顶数据 , 命名为data.py
1.1定义个req函数,方便读取 。//需设定编码格式 , 否则会出现乱码
def Re_data(url):
re = requests.get(url)
re.encoding = 'gbk'
data = https://www.04ip.com/post/json.loads(re.text)
return data['data']
1.2定义个Get函数,用于读取数据并使用保存函数进行保存数据 , 保存格式为json 。
def Get_data():
# 获取数据并保存至data目录
base_url = ''
chess = Re_data(base_url'chess.js')
race = Re_data(base_url'race.js')
job = Re_data(base_url'job.js')
equip = Re_data(base_url'equip.js')
Save_data(chess,race,job,equip)
1.3定义save函数实现读取的数据进行文件保存,保存目录为工作目录下的data文件夹 。
def Save_data(t_chess,t_race,t_job,t_equip):
with open('./data/chess.json','w') as f:
json.dump(t_chess,f,indent='\t')
with open('./data/race.json','w') as f:
json.dump(t_race,f,indent='\t')
with open('./data/job.json','w') as f:
json.dump(t_job,f,indent='\t')
with open('./data/equip.json','w') as f:
json.dump(t_equip,f,indent='\t')
1.4定义主函数main跑起来
if __name__ == '__main__':
start = time.time()
Get_data()
print('运行时间:'str(time.time() - start)'秒')
至此 , 数据爬取完成 。
2.种族和职业进行组合 。
2.1未完成 //未完成,使用穷举方法进行组合会出现内存不够导致组合失败(for循环嵌套导致数组内存超限)
//待学习 , 使用知识图谱建立组合优?。刹慰迹?
期间遇到的问题:
1.爬取棋子数据时为动态js加载 , 需通过json模块的loads方法获取
2.3层for循环嵌套数据量大,导致计算失败,需优化计算方法 。
如何用Python做爬虫?在我们日常上网浏览网页的时候python爬取函数实现,经常会看到一些好看的图片,我们就希望把这些图片保存下载,或者用户用来做桌面壁纸,或者用来做设计的素材 。
我们最常规的做法就是通过鼠标右键,选择另存为 。但有些图片鼠标右键的时候并没有另存为选项,还有办法就通过就是通过截图工具截取下来 , 但这样就降低图片的清晰度 。好吧其实你很厉害的,右键查看页面源代码 。
我们可以通过python 来实现这样一个简单的爬虫功能,把我们想要的代码爬取到本地 。下面就看看如何使用python来实现这样一个功能 。
具体步骤
获取整个页面数据首先我们可以先获取要下载图片的整个页面信息 。
getjpg.py
#coding=utf-8import urllibdef getHtml(url):
page = urllib.urlopen(url)
html = page.read()return html
html = getHtml("")print html
Urllib 模块提供python爬取函数实现了读取web页面数据的接口,我们可以像读取本地文件一样读取www和ftp上的数据 。首先,我们定义python爬取函数实现了一个getHtml()函数:
urllib.urlopen()方法用于打开一个URL地址 。
read()方法用于读取URL上的数据,向getHtml()函数传递一个网址,并把整个页面下载下来 。执行程序就会把整个网页打印输出 。
2.筛选页面中想要的数据
Python 提供了非常强大的正则表达式 , 我们需要先要了解一点python 正则表达式的知识才行 。
假如我们百度贴吧找到了几张漂亮的壁纸 , 通过到前段查看工具 。找到了图片的地址,如python爬取函数实现:src=https://www.04ip.com/post/””pic_ext=”jpeg”
修改代码如下python爬取函数实现:
import reimport urllibdef getHtml(url):
page = urllib.urlopen(url)
html = page.read()return htmldef getImg(html):
reg = r'src="https://www.04ip.com/post/(. ?\.jpg)" pic_ext'
imgre = re.compile(reg)
imglist = re.findall(imgre,html)return imglist
html = getHtml("")print getImg(html)
我们又创建了getImg()函数 , 用于在获取的整个页面中筛选需要的图片连接 。re模块主要包含了正则表达式:
re.compile() 可以把正则表达式编译成一个正则表达式对象.
re.findall() 方法读取html 中包含 imgre(正则表达式)的数据 。
运行脚本将得到整个页面中包含图片的URL地址 。
3.将页面筛选的数据保存到本地
把筛选的图片地址通过for循环遍历并保存到本地,代码如下:
#coding=utf-8import urllibimport redef getHtml(url):
page = urllib.urlopen(url)
html = page.read()return htmldef getImg(html):
reg = r'src="https://www.04ip.com/post/(. ?\.jpg)" pic_ext'
imgre = re.compile(reg)
imglist = re.findall(imgre,html)
x = 0for imgurl in imglist:
urllib.urlretrieve(imgurl,'%s.jpg' % x)
x =1html = getHtml("")print getImg(html)
这里的核心是用到了urllib.urlretrieve()方法,直接将远程数据下载到本地 。
通过一个for循环对获取的图片连接进行遍历 , 为了使图片的文件名看上去更规范,对其进行重命名,命名规则通过x变量加1 。保存的位置默认为程序的存放目录 。
程序运行完成,将在目录下看到下载到本地的文件 。
如何用python爬取豆瓣读书的数据这两天爬了豆瓣读书的十万条左右的书目信息,用时将近一天 , 现在趁着这个空闲把代码总结一下,还是菜鸟,都是用的最简单最笨的方法,还请路过的大神不吝赐教 。
第一步,先看一下我们需要的库:
import requests#用来请求网页
from bs4 import BeautifulSoup#解析网页
import time#设置延时时间 , 防止爬取过于频繁被封IP号
import re#正则表达式库
import pymysql#由于爬取的数据太多,我们要把他存入MySQL数据库中,这个库用于连接数据库
import random#这个库里用到了产生随机数的randint函数,和上面的time搭配 , 使爬取间隔时间随机
这个是豆瓣的网址:x-sorttags-all
我们要从这里获取所有分类的标签链接,进一步去爬取里面的信息,代码先贴上来:
import requests
from bs4 import BeautifulSoup#导入库
url="httom/tag/?icn=index-nav"
wb_data=https://www.04ip.com/post/requests.get(url)#请求网址
soup=BeautifulSoup(wb_data.text,"lxml")#解析网页信息
tags=soup.select("#contentdivdiv.articledivdivtabletbodytrtda")
#根据CSS路径查找标签信息,CSS路径获取方法,右键-检查-copy selector,tags返回的是一个列表
for tag in tags:
tag=tag.get_text()#将列表中的每一个标签信息提取出来
helf="hom/tag/"
#观察一下豆瓣的网址,基本都是这部分加上标签信息,所以我们要组装网址,用于爬取标签详情页
url=helf str(tag)
print(url)#网址组装完毕,输出
以上我们便爬取了所有标签下的网址 , 我们将这个文件命名为channel,并在channel中创建一个channel字符串,放上我们所有爬取的网址信息,等下爬取详情页的时候直接从这里提取链接就好了,如下:
channel='''
tag/程序
'''
现在,我们开始第二个程序 。
QQ图片20160915233329.png
标签页下每一个图片的信息基本都是这样的,我们可以直接从这里提取到标题,作者,出版社,出版时间,价格,评价人数 , 以及评分等信息(有些外国作品还会有译者信息),提取方法与提取标签类似,也是根据CSS路径提取 。
我们先用一个网址来实验爬?。?
url="htt/tag/科技"
wb_data = https://www.04ip.com/post/requests.get(url)
soup = BeautifulSoup(wb_data.text.encode("utf-8"), "lxml")
tag=url.split("?")[0].split("/")[-1]#从链接里面提取标签信息 , 方便存储
detils=soup.select("#subject_listullidiv.infodiv.pub")#抓取作者 , 出版社信息 , 稍后我们用spite()函数再将他们分离出来
scors=soup.select("#subject_listullidiv.infodiv.star.clearfixspan.rating_nums")#抓取评分信息
persons=soup.select("#subject_listullidiv.infodiv.star.clearfixspan.pl")#评价人数
titles=soup.select("#subject_listullidiv.infoh2a")#书名
#以上抓取的都是我们需要的html语言标签信息,我们还需要将他们一一分离出来
for detil,scor,person,title in zip(detils,scors,persons,titles):
#用一个zip()函数实现一次遍历
#因为一些标签中有译者信息,一些标签中没有,为避免错误,所以我们要用一个try来把他们分开执行
try:
author=detil.get_text().split("/",4)[0].split()[0]#这是含有译者信息的提取办法 , 根据“/”把标签分为五部分,然后依次提取出来
yizhe= detil.get_text().split("/", 4)[1]
publish=detil.get_text().split("/", 4)[2]
time=detil.get_text().split("/", 4)[3].split()[0].split("-")[0]#时间我们只提取了出版年份
price=ceshi_priceone(detil)#因为价格的单位不统一,我们用一个函数把他们换算为“元”
scoe=scor.get_text() if True else ""#有些书目是没有评分的,为避免错误,我们把没有评分的信息设置为空
person=ceshi_person(person)#有些书目的评价人数显示少于十人,爬取过程中会出现错误,用一个函数来处理
title=title.get_text().split()[0]
#当没有译者信息时,会显示IndexError,我们分开处理
except IndexError:
try:
author=detil.get_text().split("/", 3)[0].split()[0]
yizhe=""#将detil信息划分为4部分提?。胝咝畔⒅苯由柚梦? ,其他与上面一样
publish=detil.get_text().split("/", 3)[1]
time=detil.get_text().split("/", 3)[2].split()[0].split("-")[0]
price=ceshi_pricetwo(detil)
scoe=scor.get_text() if True else ""
person=ceshi_person(person)
title=title.get_text().split()[0]
except (IndexError,TypeError):
continue
#出现其他错误信息,忽略,继续执行(有些书目信息下会没有出版社或者出版年份,但是数量很少,不影响我们大规模爬取,所以直接忽略)
except TypeError:
continue
#提取评价人数的函数 , 如果评价人数少于十人,按十人处理
def ceshi_person(person):
try:
person = int(person.get_text().split()[0][1:len(person.get_text().split()[0]) - 4])
except ValueError:
person = int(10)
return person
#分情况提取价格的函数,用正则表达式找到含有特殊字符的信息,并换算为“元”
def ceshi_priceone(price):
price = detil.get_text().split("/", 4)[4].split()
if re.match("USD", price[0]):
price = float(price[1]) * 6
elif re.match("CNY", price[0]):
price = price[1]
elif re.match("\A$", price[0]):
price = float(price[1:len(price)]) * 6
else:
price = price[0]
return price
def ceshi_pricetwo(price):
price = detil.get_text().split("/", 3)[3].split()
if re.match("USD", price[0]):
price = float(price[1]) * 6
elif re.match("CNY", price[0]):
price = price[1]
elif re.match("\A$", price[0]):
price = float(price[1:len(price)]) * 6
else:
price = price[0]
return price
实验成功后 , 我们就可以爬取数据并导入到数据库中了,以下为全部源码,特殊情况会用注释一一说明 。
import requests
from bs4 import BeautifulSoup
import time
import re
import pymysql
from channel import channel#这是我们第一个程序爬取的链接信息
import random
def ceshi_person(person):
try:
person = int(person.get_text().split()[0][1:len(person.get_text().split()[0]) - 4])
except ValueError:
person = int(10)
return person
def ceshi_priceone(price):
price = detil.get_text().split("/", 4)[4].split()
if re.match("USD", price[0]):
price = float(price[1]) * 6
elif re.match("CNY", price[0]):
price = price[1]
elif re.match("\A$", price[0]):
price = float(price[1:len(price)]) * 6
else:
price = price[0]
return price
def ceshi_pricetwo(price):
price = detil.get_text().split("/", 3)[3].split()
if re.match("USD", price[0]):
price = float(price[1]) * 6
elif re.match("CNY", price[0]):
price = price[1]
elif re.match("\A$", price[0]):
price = float(price[1:len(price)]) * 6
else:
price = price[0]
return price
#这是上面的那个测试函数 , 我们把它放在主函数中
def mains(url):
wb_data = https://www.04ip.com/post/requests.get(url)
soup = BeautifulSoup(wb_data.text.encode("utf-8"), "lxml")
tag=url.split("?")[0].split("/")[-1]
detils=soup.select("#subject_listullidiv.infodiv.pub")
scors=soup.select("#subject_listullidiv.infodiv.star.clearfixspan.rating_nums")
persons=soup.select("#subject_listullidiv.infodiv.star.clearfixspan.pl")
titles=soup.select("#subject_listullidiv.infoh2a")
for detil,scor,person,title in zip(detils,scors,persons,titles):
l = []#建一个列表 , 用于存放数据
try:
author=detil.get_text().split("/",4)[0].split()[0]
yizhe= detil.get_text().split("/", 4)[1]
publish=detil.get_text().split("/", 4)[2]
time=detil.get_text().split("/", 4)[3].split()[0].split("-")[0]
price=ceshi_priceone(detil)
scoe=scor.get_text() if True else ""
person=ceshi_person(person)
title=title.get_text().split()[0]
except IndexError:
try:
author=detil.get_text().split("/", 3)[0].split()[0]
yizhe=""
publish=detil.get_text().split("/", 3)[1]
time=detil.get_text().split("/", 3)[2].split()[0].split("-")[0]
【包含python爬取函数实现的词条】price=ceshi_pricetwo(detil)
scoe=scor.get_text() if True else ""
person=ceshi_person(person)
title=title.get_text().split()[0]
except (IndexError,TypeError):
continue
except TypeError:
continue
l.append([title,scoe,author,price,time,publish,person,yizhe,tag])
#将爬取的数据依次填入列表中
sql="INSERT INTO allbooks values(%s,%s,%s,%s,%s,%s,%s,%s,%s)"#这是一条sql插入语句
cur.executemany(sql,l)#执行sql语句 , 并用executemary()函数批量插入数据库中
conn.commit()
#主函数到此结束
# 将Python连接到MySQL中的python数据库中
conn = pymysql.connect( user="root",password="123123",database="python",charset='utf8')
cur = conn.cursor()
cur.execute('DROP TABLE IF EXISTS allbooks')#如果数据库中有allbooks的数据库则删除
sql = """CREATE TABLE allbooks(
title CHAR(255) NOT NULL,
scor CHAR(255),
author CHAR(255),
price CHAR(255),
time CHAR(255),
publish CHAR(255),
person CHAR(255),
yizhe CHAR(255),
tag CHAR(255)
)"""
cur.execute(sql)#执行sql语句,新建一个allbooks的数据库
start = time.clock()#设置一个时钟,这样我们就能知道我们爬取了多长时间了
for urls in channel.split():
urlss=[urls "?start={}type=T".format(str(i)) for i in range(0,980,20)]#从channel中提取url信息,并组装成每一页的链接
for url in urlss:
mains(url)#执行主函数,开始爬取
print(url)#输出要爬取的链接,这样我们就能知道爬到哪了,发生错误也好处理
time.sleep(int(format(random.randint(0,9))))#设置一个随机数时间 , 每爬一个网页可以随机的停一段时间,防止IP被封
end = time.clock()
print('Time Usage:', end - start)#爬取结束,输出爬取时间
count = cur.execute('select * from allbooks')
print('has %s record' % count)#输出爬取的总数目条数
# 释放数据连接
if cur:
cur.close()
if conn:
conn.close()
这样,一个程序就算完成了 , 豆瓣的书目信息就一条条地写进了我们的数据库中,当然,在爬取的过程中,也遇到了很多问题,比如标题返回的信息拆分后中会有空格,写入数据库中会出现错误 , 所以只截取了标题的第一部分,因而导致数据库中的一些书名不完整,过往的大神如果有什么办法,还请指教一二 。
等待爬取的过程是漫长而又欣喜的,看着电脑上一条条信息被刷出来,成就感就不知不觉涌上心头;然而如果你吃饭时它在爬,你上厕所时它在爬,你都已经爬了个山回来了它还在爬时 , 便会有点崩溃了,担心电脑随时都会坏掉(还是穷学生换不起啊啊啊啊~)
所以,还是要好好学学设置断点,多线程,以及正则,路漫漫其修远兮,吾将上下而求索~共勉~
零基础学python(1)——爬取房天下网站信息一、认识网页
网页分为三个部分:HTML(结构)、CSS(样式)、JavaScript(功能) 。
二、爬取网站信息入门
1、Soup = BeautifulSoup (html, 'lxml') , 使用beautifulsoup来解析网页 。
2、使用copy CSS selector来复制网页元素的位置 。
三、爬取房天下网站信息
1、导入requests和beautifulsoup
2、定义函数spider_ftx , 把所需要爬取的信息都定义出来
3、调用函数spider_ftx
4、翻页爬取二手房信息
由于每页最多只能显示40条信息 , 观察每一页网址的变化规律,写一个循环调用的语句,把全部100页的信息全都爬取下来 。
四、小结:
目前只能爬取到网站的100页信息,网站为了反爬,设置了可浏览的页面量100 。要想爬取网站的所有信息,可以通过分类去获?。侨绾斡胮ython实现呢,请看下集 。
怎么使用python来爬取网页上的表格信息稍微说一下背景,当时我想研究蛋白质与小分子的复合物在空间三维结构上的一些规律,首先得有数据啊,数据从哪里来?就是从一个涵盖所有已经解析三维结构的蛋白质-小分子复合物的数据库里面下载 。这时候,手动一个个去下显然是不可取的,我们需要写个脚本,能从特定的网站选择性得批量下载需要的信息 。python是不错的选择 。
import urllib#python中用于获取网站的模块
import urllib2, cookielib
有些网站访问时需要cookie的,python处理cookie代码如下:
cj = cookielib.CookieJar ( )
opener =urllib2.build_opener( urllib2.HttpCookieProcessor(cj) )
urllib2.install_opener (opener)
通常我们需要在网站中搜索得到我们需要的信息,这里分为二种情况:
1. 第一种,直接改变网址就可以得到你想要搜索的页面:
def GetWebPage( x ):#我们定义一个获取页面的函数,x 是用于呈递你在页面中搜索的内容的参数
url = ';'‘你想要搜索的参数’# 结合自己页面情况适当修改
page = urllib2.urlopen(url)
pageContent = page.read( )
return pageContent#返回的是HTML格式的页面信息
2.第二种 , 你需要用到post方法,将你搜索的内容放在postdata里面,然后返回你需要的页面
def GetWebPage( x ):#我们定义一个获取页面的函数,x 是用于呈递你在页面中搜索的内容的参数
url =''#这个网址是你进入搜索界面的网址
postData = https://www.04ip.com/post/urllib.urlencode( {各种‘post’参数输入 } )#这里面的post参数输入需要自己去查
req= urllib2.Request (url, postData)
pageContent = urllib2.urlopen (req). read( )
return pageContent#返回的是HTML格式的页面信息
在获取了我们需要的网页信息之后,我们需要从获得的网页中进一步获取我们需要的信息,这里我推荐使用 BeautifulSoup 这个模块,python自带的没有 , 可以自行百度谷歌下载安装 。BeautifulSoup 翻译就是‘美味的汤’,你需要做的是从一锅汤里面找到你喜欢吃的东西 。
import re# 正则表达式,用于匹配字符
from bs4 import BeautifulSoup# 导入BeautifulSoup 模块
soup =BeautifulSoup(pageContent)#pageContent就是上面我们搜索得到的页面
soup就是 HTML 中所有的标签(tag)BeautifulSoup处理格式化后的字符串,一个标准的tag形式为:
hwkobe24
通过一些过滤方法,我们可以从soup中获取我们需要的信息:
(1) find_all ( name , attrs , recursive , text , **kwargs)
这里面,我们通过添加对标签的约束来获取需要的标签列表 , 比如 soup.find_all ('p') 就是寻找名字为‘p’的 标签,而soup.find_all (class = "tittle") 就是找到所有class属性为"tittle" 的标签,以及soup.find_all ( class = re.compile('lass')) 表示 class属性中包含‘lass’的所有标签 , 这里用到了正则表达式(可以自己学习一下,非常有用滴)
当我们获取了所有想要标签的列表之后,遍历这个列表,再获取标签中你需要的内容 , 通常我们需要标签中的文字部分,也就是网页中显示出来的文字,代码如下:
tagList = soup.find_all (class="tittle")#如果标签比较复杂,可以用多个过滤条件使过滤更加严格
for tag in tagList:
print tag.text
f.write ( str(tag.text) )#将这些信息写入本地文件中以后使用
(2)find( name , attrs , recursive , text , **kwargs )
它与 find_all( ) 方法唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果
(3)find_parents( )find_parent( )
find_all() 和 find() 只搜索当前节点的所有子节点,孙子节点等. find_parents() 和 find_parent() 用来搜索当前节点的父辈节点,搜索方法与普通tag的搜索方法相同,搜索文档搜索文档包含的内容
(4)find_next_siblings()find_next_sibling()
这2个方法通过 .next_siblings 属性对当 tag 的所有后面解析的兄弟 tag 节点进代, find_next_siblings() 方法返回所有符合条件的后面的兄弟节点,find_next_sibling() 只返回符合条件的后面的第一个tag节点
(5)find_previous_siblings()find_previous_sibling()
这2个方法通过 .previous_siblings 属性对当前 tag 的前面解析的兄弟 tag 节点进行迭代, find_previous_siblings()方法返回所有符合条件的前面的兄弟节点, find_previous_sibling() 方法返回第一个符合条件的前面的兄弟节点
(6)find_all_next()find_next()
这2个方法通过 .next_elements 属性对当前 tag 的之后的 tag 和字符串进行迭代, find_all_next() 方法返回所有符合条件的节点, find_next() 方法返回第一个符合条件的节点
(7)find_all_previous() 和 find_previous()
这2个方法通过 .previous_elements 属性对当前节点前面的 tag 和字符串进行迭代, find_all_previous() 方法返回所有符合条件的节点, find_previous()方法返回第一个符合条件的节点
具体的使用方法还有很多,用到这里你应该可以解决大部分问题了,如果要更深入了解可以参考官方的使用说明哈!
如何用Python做爬虫1)首先你要明白爬虫怎样工作 。
想象你是一只蜘蛛,现在你被放到了互联“网”上 。那么,你需要把所有的网页都看一遍 。怎么办呢?没问题呀,你就随便从某个地方开始,比如说人民日报的首页 , 这个叫initial pages,用$表示吧 。
在人民日报的首页,你看到那个页面引向的各种链接 。于是你很开心地从爬到了“国内新闻”那个页面 。太好了,这样你就已经爬完了俩页面(首页和国内新闻)!暂且不用管爬下来的页面怎么处理的,你就想象你把这个页面完完整整抄成了个html放到了你身上 。
突然你发现 , 在国内新闻这个页面上,有一个链接链回“首页” 。作为一只聪明的蜘蛛,你肯定知道你不用爬回去的吧 , 因为你已经看过了啊 。所以,你需要用你的脑子,存下你已经看过的页面地址 。这样 , 每次看到一个可能需要爬的新链接,你就先查查你脑子里是不是已经去过这个页面地址 。如果去过,那就别去了 。
好的,理论上如果所有的页面可以从initial page达到的话,那么可以证明你一定可以爬完所有的网页 。
那么在python里怎么实现呢?
很简单
import Queue
initial_page = "初始化页"
url_queue = Queue.Queue()
seen = set()
seen.insert(initial_page)
url_queue.put(initial_page)
while(True): #一直进行直到海枯石烂
if url_queue.size()0:
current_url = url_queue.get()#拿出队例中第一个的url
store(current_url)#把这个url代表的网页存储好
for next_url in extract_urls(current_url): #提取把这个url里链向的url
if next_url not in seen:
seen.put(next_url)
url_queue.put(next_url)
else:
break
写得已经很伪代码了 。
所有的爬虫的backbone都在这里,下面分析一下为什么爬虫事实上是个非常复杂的东西——搜索引擎公司通常有一整个团队来维护和开发 。
2)效率
如果你直接加工一下上面的代码直接运行的话 , 你需要一整年才能爬下整个豆瓣的内容 。更别说Google这样的搜索引擎需要爬下全网的内容了 。
问题出在哪呢?需要爬的网页实在太多太多了,而上面的代码太慢太慢了 。设想全网有N个网站 , 那么分析一下判重的复杂度就是N*log(N),因为所有网页要遍历一次,而每次判重用set的话需要log(N)的复杂度 。OK , OK,我知道python的set实现是hash——不过这样还是太慢了,至少内存使用效率不高 。
通常的判重做法是怎样呢?Bloom Filter. 简单讲它仍然是一种hash的方法 , 但是它的特点是,它可以使用固定的内存(不随url的数量而增长)以O(1)的效率判定url是否已经在set中 。可惜天下没有白吃的午餐,它的唯一问题在于,如果这个url不在set中,BF可以100%确定这个url没有看过 。但是如果这个url在set中,它会告诉你:这个url应该已经出现过 , 不过我有2%的不确定性 。注意这里的不确定性在你分配的内存足够大的时候,可以变得很小很少 。一个简单的教程:Bloom Filters by Example
注意到这个特点,url如果被看过,那么可能以小概率重复看一看(没关系,多看看不会累死) 。但是如果没被看过,一定会被看一下(这个很重要,不然我们就要漏掉一些网页了?。?。[IMPORTANT: 此段有问题,请暂时略过]
好,现在已经接近处理判重最快的方法了 。另外一个瓶颈——你只有一台机器 。不管你的带宽有多大,只要你的机器下载网页的速度是瓶颈的话,那么你只有加快这个速度 。用一台机子不够的话——用很多台吧!当然,我们假设每台机子都已经进了最大的效率——使用多线程(python的话 , 多进程吧) 。
3)集群化抓取
爬取豆瓣的时候,我总共用了100多台机器昼夜不停地运行了一个月 。想象如果只用一台机子你就得运行100个月了...
那么 , 假设你现在有100台机器可以用,怎么用python实现一个分布式的爬取算法呢?
我们把这100台中的99台运算能力较小的机器叫作slave,另外一台较大的机器叫作master,那么回顾上面代码中的url_queue,如果我们能把这个queue放到这台master机器上,所有的slave都可以通过网络跟master联通 , 每当一个slave完成下载一个网页,就向master请求一个新的网页来抓取 。而每次slave新抓到一个网页,就把这个网页上所有的链接送到master的queue里去 。同样,bloom filter也放到master上,但是现在master只发送确定没有被访问过的url给slave 。Bloom Filter放到master的内存里,而被访问过的url放到运行在master上的Redis里 , 这样保证所有操作都是O(1) 。(至少平摊是O(1),Redis的访问效率见:LINSERT – Redis)
考虑如何用python实现:
在各台slave上装好scrapy , 那么各台机子就变成了一台有抓取能力的slave,在master上装好Redis和rq用作分布式队列 。
代码于是写成
#slave.py
current_url = request_from_master()
to_send = []
for next_url in extract_urls(current_url):
to_send.append(next_url)
store(current_url);
send_to_master(to_send)
#master.py
distributed_queue = DistributedQueue()
bf = BloomFilter()
initial_pages = ""
while(True):
if request == 'GET':
if distributed_queue.size()0:
send(distributed_queue.get())
else:
break
elif request == 'POST':
bf.put(request.url)
好的,其实你能想到,有人已经给你写好了你需要的:darkrho/scrapy-redis · GitHub
4)展望及后处理
虽然上面用很多“简单”,但是真正要实现一个商业规模可用的爬虫并不是一件容易的事 。上面的代码用来爬一个整体的网站几乎没有太大的问题 。
但是如果附加上你需要这些后续处理,比如
有效地存储(数据库应该怎样安排)
有效地判重(这里指网页判重,咱可不想把人民日报和抄袭它的大民日报都爬一遍)
有效地信息抽?。ū热缭趺囱槿〕鐾成纤械牡刂烦槿〕隼?,“朝阳区奋进路中华道”),搜索引擎通常不需要存储所有的信息,比如图片我存来干嘛...
及时更新(预测这个网页多久会更新一次)
如你所想,这里每一个点都可以供很多研究者十数年的研究 。虽然如此,
“路漫漫其修远兮,吾将上下而求索” 。
所以 , 不要问怎么入门,直接上路就好了:)
关于python爬取函数实现和的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。

    推荐阅读