关于最好大学排名的爬虫

概述

既然决定把视频上老师讲的实战都自己动手实现一遍,那么就先把最好大学排名这个实例自己写一遍。看视频的时候挺轻松的,但是到自己动手的时候才知道不容易,写这个程序遇到两个比较棘手的问题,一个是如何从网页中提取出自己想要的信息,另一个是信息以什么样的形式保存并展示出来。其实几乎所有的爬虫都会遇到这两个问题吧,希望在以后的练习中能不断增强对这两个问题的处理能力。当然,这个实例还有一个重要的作用是增强自己对beautifulsoup库的理解和运用。

准备工作

打开Chrome浏览器,查看要爬取的页面是否为静态页面,如果不是的话以目前的技术是不能对其进行爬取的,毕竟视频的录制已经有一段时间了,网页可能会有所变化。经检查,幸好网页还是静态的,就可以用requests+bs4对其进行爬取。

调试

不能抡起袖子,就在sublime中猛写代码,边写边改,其实效率是很低的,因为如果在编辑器中写代码调试,发现自己的代码不能抓取到目标信息,就必须修改代码并且重新对网页进行爬取操作,无疑这种人工的重复劳动是不行的,效率十分低下,毕竟只是个小白,不能一下子就把正确的信息抓取下来。
针对此问题,可以在Python shell中进行调试,这样就好了许多。在交互式窗口中引入requests和bs4,在shell中进行调试明显比在命令行中调试好了许多。
调试过程中尽量用浏览器的开发者工具对信息进行分析吧,如果用源代码对其进行分析的话,结构不清晰,不容易分析。

bd4的使用

根据个人的习惯,发现自己最常用的方法应该是find_all()、对子节点的访问和平行节点向下遍历了,有时还会遇到对标签的属性信息和文本信息的提取。

find_all()

标签对象的一个方法,主要是用来定位一个标签或者是一些标签的位置,返回的是一个列表,列表里的每个元素是一个tag对象。此方法常和标签的属性组合来完成定位,要注意的是,class属性由于和Python中的关键字冲突,所以用class_代替,由于此方法很常用,所以可以不写,直接加对括号就可以表示调用了此方法了,十分方便。

子节点遍历
  • "." 操作符 得到子节点中第一个符合的标签
  • .contents 返回一个含所有子节点的列表
  • .children 返回一个可迭代对象,内容和.contents是一样的
tbody.tr.td
<td>1</td>

值得注意的是,空字符串或者是换行符也算是节点,这是一个大坑,要注意一点。

平行节点的向下遍历
  • .next_sibling 返回一个标签对象
  • .next_siblings 返回一个可迭代对象
对标签属性的访问
  • 类似于字典式的访问
  • get()
 tbody['class']
['hidden_zhpm']
tbody.get('class')
['hidden_zhpm']

也许用第二种意思更清晰点吧==

对标签中文本信息的提取
  • .string
  • .text
  • .get_text()
td.string
'1'
td.text
'1'
td.get_text()
'1'

遇到的问题

  1. 每所学校的标签基本上一样的,有些有属性还能精准定位,而有些没有属性,不能精准的定位,而且还有些空格或者是换行符还混进来,增加了提取信息的难度。
  2. 每所学校要获取的信息有三个:排名、学校名称和所在地区,应该以哪种方式对它们进行保存也是个问题。
  3. 把获取到的信息格式化输出也是个问题,因为要做到简洁、清晰和美观。
  • 对于第一个问题,既然有些标签没有属性,不能精确定位,又不能将其放弃,那么只能不用配合属性将它们都抓取下来,其中混杂着换行符这个垃圾信息,可以增加一个判断语句来将换行符给排除掉:
    if isinstance(tr, bs4.element.Tag)
  • 第二个问题,用列表的形式将这三个信息保存下来,而这三个元素是列表的一个元素,也就是嵌套列表,列表可以从主函数这边传递过去。
  • 第三个问题,格式化输出可以用字符串的format函数,但是还是遇到了问题,主要是大学名称太长会把后面的省市的一部分空间给占用掉,造成不对齐现象。测试了很多次,在设置的长度足够长的情况下,只要中文字数超过7个,必将不对齐,很神奇的现象,跑去论坛问,也没有谁说出个所以然来,很无奈。format函数在一般情况下不会出现问题,先这样吧。

总结

对于这个爬虫程序的编写,最大的收获就是帮助我理解和掌握beautifulsoup的用法,当然还有其他方面的收获,比如怎样将信息格式化输出和学习老师编写代码的方法,虽然最后格式化输出有点不理想,但整个程序在整体上还是成功的,希望自己继续努力。

源代码

from fake_useragent import UserAgent
import requests
from requests import Timeout, HTTPError
from bs4 import BeautifulSoup
import bs4


def get_page(url, ua):		
	try:
		headers = {'User-Agent':ua}
		response = requests.get(url, headers=headers, timeout=10)
		response.raise_for_status()
		response.encoding = 'utf-8'
		return response.text
	except Timeout:
		print('requests timeout')
		get_page(url)
	except HTTPError:
		print('the http error(maybe status is not 200)')
	except:
		print('other error')

def parse_page(html):
	soup = BeautifulSoup(html, 'html.parser')
	return soup 

def get_message(html, ulist):
	tbody = html.find('tbody', class_='hidden_zhpm')
	trs = tbody('tr')
	for tr in trs:
		if isinstance(tr, bs4.element.Tag):
			tds = tr.contents
			ulist.append([tds[2].string.strip(),tds[4].string.strip(), tds[0].string.strip()])

def print_message(messages):
	templet = '{0:<50}\t{1:<50}\t{2:<50}'
	print(templet.format('排名', '学校名称', '省市'))
	for ult in messages:
		print(templet.format(ult[2], ult[0], ult[1], ' '))

		
def main():
	ua = UserAgent()
	ua = ua.random
	url = 'http://www.zuihaodaxue.com/zuihaodaxuepaiming2016.html'
	html = get_page(url, ua)
	soup = parse_page(html)
	ulist = []
	get_message(soup, ulist)
	print_message(ulist)

main()
posted @ 2017-12-30 21:19  耳锅  阅读(452)  评论(0编辑  收藏  举报