编译环境:python v3.5.0, mac osx 10.11.4
python爬虫基础知识: Python爬虫学习-基础爬取
了解数据库 MongoDB
数据库是储存数据的地方,可以将如下的字典结构插入到MongoDB的存储单元中。
data = {
'name':peter
'id':123
...
} # 需存储的文件
数据库的构成:可以将其类比于excel表格进行理解
client = pymongo.MongoClient('localhost',27017) # 将python与mongodb进行连接,'localhost'表示本地环境, 207017是端口号
walden = client['walden'] # 创建一个库文件
以上代码可以类似于创建一个excel文件,文件名为walden
sheet_tab = walden['sheet_tab'] # 在库文件中建立一个页面名叫 sheet_tab
以上代码可以类似于创建excel文件中的一个表单
数据库的基本操作:
向页边中插入数据:sheet_tab.insert_one(data)
其中data为python中的字典结构,可有如下代码生成:
path = './walden.txt' # 输入数据的路径,为读取数据做准备
with open(path,'r') as f: # 打开文件,为只读模式
lines = f.readlines()
for index,line in enumerate(lines): # 逐个生成字典元素
data = {
'index':index,
'line' :line,
'words':len(line.split())
}
sheet_tab.insert_one(data) # 将字典元素插入库文件页面中
筛选数据库中的数据(基础筛选)
sheet_tab.find({'words':{'$lt':5}} # 选择字典中关键字words对应值小于5的所有字典元素
# $lt/$lte/$gt/$gte/$ne,依次等价于<=/>/>=/!=。(l表示less g表示greater e表示equal n表示not )
基础实战(筛选房源)
筛选小猪短租网站前三页信息储存到MongoDB中,筛选出价格大于等于500元房源,并打印出来。房源信息具体要求如下:
实战源码 (下载地址xiaozhu.py)
# -- coding: utf-8 --
import requests, time, pymongo
from bs4 import BeautifulSoup
def gender_info(soup): # 获取性别信息
gender = 'female' if soup.find_all('div','div.member_ico1') else 'male'
return gender
def get_info(url): # 获取所需的房源信息
wb_data = requests.get(url) # 向服务器请求页面
wb_data.encoding ='utf-8' # 标明编码为utf-8,以免出现解码错误
soup = BeautifulSoup(wb_data.text,'lxml') # 以lxml方式对页面进行解析
title = soup.select('h4 em')[0].text
address = soup.select('span.pr5')[0].text
price = int(soup.select('div.day_l span')[0].text)
img = soup.select('#curBigImage')[0].get('src')
hostPic = soup.select('#floatRightBox > div.js_box.clearfix > div.member_pic > a > img')[0].get('src')
hostName = soup.select('#floatRightBox > div.js_box.clearfix > div.w_240 > h6 > a')[0].text
hostGender = gender_info(soup)
data = {
'title' : title,
'address': address,
'price' : price,
'img' :img,
'hostPic' : hostPic,
'hostName' : hostName,
'hostGender' : hostGender
}
print('get_info Done')
return data
def get_list_url(pageURL): # 获取页面中所有详细房源的url
listUrl = []
wb_data = requests.get(pageURL)
wb_data.encoding = 'utf-8'
soup = BeautifulSoup(wb_data.text,'lxml')
pageList = soup.select('div.result_btm_con.lodgeunitname')
for i in pageList:
listUrl.append(i.get('detailurl'))
print('get_list_url Done')
return listUrl
def get_info_by_page(startPage, endPage, baseURL,database): # 获取整个页面的信息
for i in range(startPage,endPage+1):
url = baseURL.format(i)
listUrl = get_list_url(url)
for j in listUrl:
time.sleep(4)
dataInfo = get_info(j) # 获取每个页面的信息
database.insert_one(dataInfo) # 将信息插入到指定的页面中
print('input to database Done')
client = pymongo.MongoClient('localhost',27017) # 连接mongodb
xiaozhu = client['xiaozhu'] # 创建一个名叫xiaozhu的库文件
home_info = xiaozhu['home_info'] # 创建一个home_info的页面
pageBaseUrl = 'http://bj.xiaozhu.com/search-duanzufang-p{}-0/' # 构造共同url连接
get_info_by_page(1,3,pageBaseUrl,home_info) # 调用函数爬取信息并将信息储存到mongodb中
for info in home_info.find({'price':{'$gte':500}}): # 打印大于等于500的房源信息
print(info)
结果展示
mongoDB中的储存结果(部分截图)
价格大于等于500的房源信息(部分截图)
爬取工作分析流程
1. 观察页面特征,保证爬虫程序的通用性,即:发现边界条件和局限性。
例:爬取赶集网-北京二手市场的所有类目中属于个人的商品信息。
观察的到页面(url)变动的信息
发现页面变动边界条件
当把页面设定到150页时,我们发现返回的页面是任意四件商品的信息。因此,我们要据此,判断我们所爬取的页面是否已经到头。避免重复的信息加入到我们的数据库中。
并且通过观察发现网站这一返回操作,我们发现正常页面中有列表链接可以点击,而由于页面超出范围返回的随机商品信息页面没有。
因此我们可以用BeautifulSoup库中的find方法实现这个操作。
soup.find('ul', 'pageLink') #找到返回TRUE,没有返回FALSE
一般这种交易网站,当商品卖出后,商品有关信息页面将会被删除,所以我们爬取的过程中,可能将有商品被卖出,当我们向服务器进行请求该商品详情界面时会出现404 not found。我们可以通过status_code的方法对页面进行判断。
wb_data.status_code == 404 # 判断商品是否已被卖出,卖出则返回TRUE,没有则返回FALSE
2. 设计工作流程,保证输出效率和稳定性。
分步进行:先获取channel_list(所有频道分类的URL),保证爬取的稳定性。若是爬取类目信息,与爬取商品信息同步进行的话,当程序出现错误时,我们则什么信息也不能得到。所以分步进行可以降低风险。(图中分类项目下的所有商品详情链接)
多进程爬取: 可以利用multiprocess库中的pool函数,进行多进程爬取,这样可以提高爬取的效率。
关于进程与线程:
可以理解成多个人完成吃饭这个工作的效率:
单线程单进程:只有一个餐桌,一个人在一个餐桌上吃饭,每个人依次进行。
单线程多进程:有多个餐桌,每个餐座上只有一个人在吃饭。
单进程多线程: 只有一个餐桌,一个餐桌上可以坐多个人。
多进程多线程:多个餐座,一个餐桌上可以坐多个人。
对项目进行监测:
我们可以设计一个检测函数,隔一段时间汇报所抓取信息的数量,对项目进程进行掌控。
import timeframe page_parsing
import url_list
while True:
print(url_list.find().count())
time.sleep(5)
设计断点续传程序:
由于在我们抓取的过程中可能会遇到网络问题,导致程序终止,而我们不希望重新开始抓取,而是在中断后的地方继续进行抓取。
设计思路如下:
数据库中建立两个页面存放详情商品链接(从这一点也可以看出分步抓取的重要性)。一个存放需要抓取的(url_list1)一个存放已经抓取网商品信息的 (url_list2)。
当中断后继续抓取时,url_list1-url_list2就是剩下带抓取的商品信息页面。
db_urls = [item['url'] for item in url_list.find()] # 用列表解析式装入所有要爬取的链接
index_urls = [item['url'] for item in item_info.find()] # 所引出详情信息数据库中所有的现存的 url 字段
x = set(db_urls) # 转换成集合的数据结构
y = set(index_urls)rest_of_urls = x-y # 剩下的url
爬取结果以及源码(按设计步骤展示)
**All source code **: JacobKam-GitHub