做文本分析时,在很多情况下,需要我们自己去爬外部网站的数据,由于不同的网站有不同的网页结构,所以处理起来就需要根据网页不同去做解析。解析网页使用的语言为python,因为他们提供了很多的类似插件的工具包,最终比较一下,使用的是requests包和soupy包,网页爬的数据做为以后文本分析处理的基础,关于本篇文章使用的工具包的相关参考文档如下:
http://www.python-requests.org/en/latest/
http://soupy.readthedocs.org/en/stable/index.html
下面以两个例子来说明:解析豆瓣网站的书评,把用户评分,评分时间和评分内空取出来,并保存成cvs格式的文档。爬豆瓣网书评时需要注意服务器会返回403错误,还有就是保存文件时可能会因为编码问题导致csv文件保存失败。实现部分请直接看代码:
我们以http://book.douban.com/subject/26425831/comments/为例,做数据抓取:
def get_db_book_review(data_list, item_id, page):
# 需要抓取网页的URL地址
target_url = "http://book.douban.com/subject/" + str(item_id) + "/comments/hot?p=" + str(page)
logging.info("当前抓取的网页地址为:" + target_url)
# 设置User-Agent,如果没有配会报403错误
client = requests.session()
client.headers = {'User-Agent': 'Reddit API - update profile'}
target_response = client.get(target_url)
logging.info(target_response.status_code)
if (200 == target_response.status_code):
douban_html = Soupy(target_response.content, "html.parser")
#使用类似jquery查找的方式来解析网页
review_items = douban_html.find(id='comment-list-wrapper').find('div', 'comment-list hot ').find('ul').find_all('li')
logging.info(review_items.count().val())
for review in review_items:
#每一条评论的字典,按cvs官方文档的要求传数据
review_dic = {}
user_stars = ""
user_date = ""
user_comment = ""
user_stars_date = review.find('span', 'comment-info').find_all('span')
user_comment = review.find('p', 'comment-content').text.val()
if user_stars_date.count().val() == 2:
user_stars = user_stars_date[0].attrs.val().get("class")[1]
user_date = user_stars_date[1].text.val()
if user_stars_date.count().val() == 1:
user_date = user_stars_date[0].text.val()
review_dic["UserStar"] = user_stars
review_dic["userDate"] = user_date
review_dic["UserComment"] = user_comment
data_list.append(review_dic)
else:
logging.info("服务器暂时无法访问!")
logging.info("=========当前处理数据完毕==================")
def export_data_to_csv(data_list, item_id):
# 设定文件的编码
with open(str(item_id) + '.csv', 'w', encoding='utf-8') as csv_file:
fieldnames = ['UserStar', 'userDate', 'UserComment']
dict_writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
dict_writer.writeheader()
dict_writer.writerows(data_list)
#item_id是书的id号
#total_page是取多少页的数据
def get_data_by_id_pages(data_list, item_id, total_page):
for page in range(1, total_page + 1):
get_db_book_review(data_list, item_id, page)
# 为了避免被服务器封ip,每间隔10秒钟抓一次数据
time.sleep(10)
export_data_to_csv(data_list, item_id)
data_list = []
get_data_by_id_pages(data_list, 26425831, 10)
生成的csv文件如下:
解析腾讯新闻的列表页,并根据列表页上的新闻的url去获取新闻详情,并保存在文本文件中。解析新闻详情时,需要过滤掉视频相关的html代码。实现部分请直接看代码:
腾讯新闻的首页为http://news.qq.com/,需要自己去找对应的html列表页:
#根据新闻列表抓取对应的新闻详情并生成文件
def get_qq_news(page):
#设定抓取数据的列表页
target_url = "http://news.qq.com/c/816guonei_"+str(page)+".htm"
logging.info("当前抓取的网页地址为:" + target_url)
target_response = requests.get(target_url)
#打印服务器返回的状态码
logging.info(target_response.status_code)
target_html = Soupy(target_response.content, "html.parser")
#类似用jquery的形式找出列表对应的html标签
news_list = target_html.find_all('div',"Q-tpList")
logging.info(news_list.count().val())
for news in news_list:
#新闻标题
news_title = news.find("div", "Q-tpWrap").find("em").find("a").text.val()
logging.info(news_title)
#新闻详情URL
detail_url = news.find("div", "Q-tpWrap").find("em").find("a").attrs.get("href").val()
detail_request_url = "http://news.qq.com"+detail_url
logging.info(detail_request_url)
detail_response = requests.get(detail_request_url)
detail_news_html = Soupy(detail_response.content.decode("gbk"), "html.parser")
#根据id='Cnt-Main-Article-QQ'找出对应的内空,因为非p标签的可能是js或视频html代码,需要过滤掉
detail_news_p_items = detail_news_html.find(id='Cnt-Main-Article-QQ').find_all('p')
news_content_data = ""
for p_item in detail_news_p_items:
logging.info(p_item.children.each(Q.text.strip()).filter(len).val())
news_content_data = news_content_data + str(p_item.children.each(Q.text.strip()).filter(len).val())
write_text_file(detail_url,str(news_content_data))
#根据文件路径和传入的数据生成文本文件
def write_text_file(file_path,file_data):
file_ext = get_file_extension(file_path)
if len(file_path)==0:
return False
else:
current_dir = os.getcwd()
dir_array = file_path.split("/")
for index,item in enumerate(dir_array):
if len(item)>0 :
current_dir = current_dir +"\\" +item
if index==len(dir_array)-1:
file = open(current_dir.replace(file_ext,'.txt'),mode='w')
file.write(file_data)
file.close()
else:
if os.path.isdir(current_dir)==False:
os.mkdir(current_dir)
#静态页后缀
def get_file_extension(path):
return os.path.splitext(path)[1]
get_qq_news(1)
腾讯新闻解析出来的是一个文件夹的列表: