本爬虫系列入门教程假设读者仅有一点点Python基础或者近乎为零的基础。如果是有Python基础的可以跳过一些对于Python基本知识的补充。
爬虫能干什么呢?一句话概括,正常通过浏览器可以获取的数据,爬虫都可以获取。这句话可以说是包罗万象。一是说明了爬虫的本质是一个服务端,实现的功能类似于浏览器;二是说明了爬虫的界限,如果不能正常访问到的数据就不能通过爬虫获取;三是爬虫的最高境界,只要是浏览器能正常访问的都可以用爬虫获取。
下面我们讲讲爬虫的基本结构和简单实现。这篇专栏我不准备详细地讲具体怎么写爬虫,只是先用一个非常简单的实现,给大家看看爬虫是个什么样子。详细的内容我们后面一个一个慢慢说。
一、爬虫的基本结构
让我们忽略掉来自各种各样的资料对于爬虫结构的描述,把问题尽可能地描述简单一点。前面说到,爬虫是一个获取正常浏览器可以获取的数据的自动化获取程序。那么,从这个功能出发,我们需要干的事情其实就两件事情:找到我们需要的网页,然后把他们一个一个处理一遍。(这句话很重要,我们在后面的后面讲到海量数据爬取策略的时候还要回到这句话。)那么问题就来了:一,怎么找到我们需要的网页的那个入口?二,怎么处理我们需要处理的网页?
对于单个页面来说,入口的来源有两种,一种是已知地址,比如我们院的网站的教学研究人员:[教学研究人员 - WISE];另外一种是你可以通过前面爬下来的网页中获取入口,比如这个页面上所有老师的个人主页。这一点很重要,后面我们在把小爬虫逐步变大的时候还要反复回到这里。
好的,后面的部分,让我们通过解决第二个问题(处理网页)的过程,顺便看看怎么解决第一个问题(获取更多入口)。我们一起开看看一个单页爬虫是怎么实现的。
二、爬虫实例:简单的单页爬虫
好了,现在我们要处理网页了。可是网页要怎么处理呢?让我们回忆一下我们使用浏览器的过程:你先把一个地址复制进浏览器的框框里面,或者点开一个链接;然后浏览器的进度条跑一下(有可能快也有可能慢),然后我们就在浏览器里面看到了数据。首先,进度条跑的过程完成了一个对网页的请求,然后浏览器把请求下来的数据进行处理,然后就输出出来。这是一个极其简化但是不太准确的对于浏览器工作原理的描述。那么爬虫和浏览器有什么不同呢?一般来说,我们是只需要解析网页,而不需要渲染浏览器环境的;另外,我们需要特定网页的特点数据,因此要用一定的方式把数据组织并储存起来。所以,爬虫的核心模块有三个:请求、解析、储存。
首先我们给一个入口:[教学研究人员 - WISE]。这就是我们今天要处理的目标网页。我们今天要实现的目的就是把这个页面上所有老师的姓名和个人主页的链接(也就是可能的下一次爬虫的入口)提取出来。下面开始写爬虫吧~
1. 请求
这里我们使用的package是requests。这是一个第三方模块(具体怎么下载以后再说),对HTTP协议进行了高度封装,非常好用。所谓HTTP协议,简单地说就是一个请求过程。我们先不管这玩意是啥,以后再讨论。这个部分,我们要实现的目的是把网页请求(或者说下载)下来。
首先我们导入requests:
import requests
下面调用requests的get函数,把网页请求下来:
r = requests.get('http://www.wise.xmu.edu.cn/people/faculty')
返回的“r”的是一个包含了整个HTTP协议需要的各种各样的东西的对象。我们先不管都有啥,先把我们需要的网页提取出来:
html = r.content
好了,到这一步我们已经获取了网页的源代码。具体源代码是什么样的呢?右键,点击“查看源文件”或者“查看源”就可以看到
2. 解析
当然从这一大坨代码里面找信息太麻烦了。我们可以用浏览器提供的另外一个工具:审查元素。这里我们先不讲怎么使用审查元素,先从源代码里面找。找到的我们需要的信息如下:
这里我们使用bs4来解析。bs4是一个非常好的解析网页的库,后面我们会详细介绍。这次的解析先给大家看bs4里面最常用的几个BeautifulSoup对象的方法(method)。我们使用的这几个方法,主要是通过HTML的标签和标签里面的参数来定位,然后用特定方法(method)提取数据。
首先还是导入package:
from bs4 import BeautifulSoup
然后创建一个BeautifulSoup对象:
soup = BeautifulSoup(html,'html.parser') #html.parser是解析器
下面我们根据我们看到的网页提取。首先提取我复制的这部分的代码的第一行,先定位到这部分代码:
div_people_list = soup.find('div', attrs={'class': 'people_list'})
这里我们使用了BeautifulSoup对象的find方法。这个方法的意思是找到带有‘div’这个标签并且参数包含" class = 'people_list' "的HTML代码。如果有多个的话,find方法就取第一个。那么如果有多个呢?正好我们后面就遇到了,现在我们要取出所有的“a”标签里面的内容:
a_s = div_people_list.find_all('a', attrs={'target': '_blank'})
这里我们使用find_all方法取出所有标签为“a”并且参数包含“ target = ‘_blank‘ ”的代码,返回一个列表。“a”标签里面的“href”参数是我们需要的老师个人主页的信息,而标签里面的文字是老师的姓名。我们继续:
这里我们使用BeautifulSoup支持的方法,使用类似于Python字典索引的方式把“a”标签里面“href”参数的值提取出来,赋值给url(Python实际上是对对象的引用),用get_text()方法把标签里面的文字提起出来。
事实上,使用这四个方法就可以正常地解析大部分HTML了。不过如果只用这四个方法,很多程序会写的异常原始。所以我们后面再继续介绍更多解析方法。
3. 储存
这里我们先弱化一下具体的储存方法,先输出到控制台上面。我们在刚才的代码的基础上加一行代码:
使用print关键词把得到的数据print出来。让我们看看结果:
# result'''
Bluhm, Marcel /people/faculty/4b901e30_5f2a_4609_96ee_2d154954d22e.html
Bowers, Roslyn /people/faculty/47557237_8d2f_4f54_a889_7ea6b2e41c91.html
Caroline Botsford /people/faculty/b16b86f2_0eed_40da_ad4a_0b559864109e.html
鲍小佳 /people/faculty/85baa506_9087_4c89_979e_4a9fd19ff60b.html
Chang, Seong Yeon /people/faculty/d0c8f922_2c64_4bbb_99d9_b3d40a04cefe.html
蔡熙乾 /people/faculty/f642c7bf_ac86_42dd_a6ac_a85720d45c8e.html
蔡宗武 /people/faculty/55981260_ddce_43aa_97b5_3d7ad0ae2844.html
陈灯塔 /people/faculty/899ecc03_5fc6_4fd7_89d7_af93901ef6b1.html
陈国进 /people/faculty/22627c95_155a_4f18_a586_df9ac4f1d7ce.html
陈海强 /people/faculty/382f444e_a849_4818_9d34_ed6347d9cbf7.html
程立新 /people/faculty/ab01c285_bbcc_4354_b27e_3bfb947a3690.html
丁锦秀 /people/faculty/de839f24_9947_43a6_9857_580ec3d07e2d.html
董晓芳 /people/faculty/abd60523_953f_48aa_862d_4b0893c88f4a.html
范青亮 /people/faculty/739b6285_0f8e_4683_b71c_640a896b687f.html
方颖 /people/faculty/94656316_8cb0_4d02_a822_4d06c12dddbe.html
冯峥晖 /people/faculty/cb02c396_6b12_497f_a724_9127aa37da33.html
Graham, Brett /people/faculty/3af92170_844d_48a4_875f_98ed85d04207.html
耿森 /people/faculty/04a8870f_d728_4e83_ba95_1d00b53cf6df.html
龚天益 /people/faculty/d7c4cd68_7489_419a_a23a_85265e67d6f9.html
管中闵 /people/faculty/d7ecca89_f116_4757_8c1e_759e435ab813.html
郭晔 /people/faculty/69499729_7f5d_4b32_9a3d_29413a8e7f71.html
韩乾 /people/faculty/27b6b1c7_9787_4747_bd44_9f2e481b5a0f.html
韩晓祎 /people/faculty/f0852907_3fc8_422e_8517_4c2c6343d3fa.html
何亚男 /people/faculty/04cbc55f_a447_4e01_b30b_ebf9d01b6f4c.html
洪永淼 /people/faculty/07848586_e55c_4e2b_8934_4df456307d7b.html
黄娟娟 /people/faculty/e03b1af7_9119_49d8_ac3a_1fd7e4843ee2.html
薛绍杰 /people/faculty/cb05eba1_f8f9_419d_86f3_f6d7e557d006.html
荆海伟 /people/faculty/5f0a030f_bcdb_4c92_87f8_f16fc4a454cc.html
赖小琼 /people/faculty/159be896_9a94_4bb9_b967_65dacbef6332.html
李嘉楠 /people/faculty/45495497_f951_4f46_8546_17bafe4148e9.html
李梦玲 /people/faculty/44f6af2b_a423_4913_83ed_f83d0fa635e5.html
李木易 /people/faculty/386198a6_ff09_4105_8337_98be8d9eaf50.html
李培 /people/faculty/8799c2eb_2ac1_4354_b2a6_25f60eb93aa5.html
李迎星 /people/faculty/91a6e77b_4e97_4a7b_98ba_15a08f8bc5ef.html
李志文 /people/faculty/67b3184d_9989_42f1_84e4_c280962320a2.html
李智 /people/faculty/22c3d25f_913e_4f03_b40f_cd80a71a8cf7.html
廖谋华 /people/faculty/76d14b07_96cd_40de_bff1_276647bafb88.html
林光平 /people/faculty/b5a0449b_d991_4345_8802_e45be1adbda3.html
林海 /people/faculty/bf8bd4a1_dca2_4c08_8128_91120a5ffd84.html
林娟 /people/faculty/6714b425_91ba_40b4_95a1_e98299ffd21d.html
林明 /people/faculty/fadf2e50_6736_43b6_94a4_f0d69a3efb4b.html
刘鼎铭 /people/faculty/92f33f3c_4cdb_4abf_b030_8942ec17329f.html
刘继春 /people/faculty/aad04279_03c4_4182_be07_4bf5f59f0e41.html
刘婧媛 /people/faculty/c85a8534_b940_4fad_b220_1e9ed73aacf0.html
龙小宁 /people/faculty/a4ba4fec_65fa_4775_9802_0200645bca74.html
马超 /people/faculty/e1544cf5_21a6_4c35_b03f_3ba4845ef11c.html
马双鸽 /people/faculty/d8d1b107_2dfc_437a_b302_e4a1b43f0817.html
茅家铭 /people/faculty/600cf256_34ab_465b_8f60_d7a15bf56934.html
蒙莉娜 /people/faculty/61cd935f_51d0_4891_805e_b43354688765.html
孟磊 /people/faculty/78bdea7b_42dd_456c_bbc2_02c1db9ff980.html
牛霖琳 /people/faculty/3dd722d5_3c4f_41c2_a7bc_c69948e4714f.html
潘越 /people/faculty/a2c16c29_ba98_4883_bf69_a63d7de6c43f.html
屈文洲 /people/faculty/7b9aa674_62fb_4acf_b293_ff569f0a991a.html
任宇 /people/faculty/b134ac71_c990_4578_a730_4e90e1c50a4d.html
White, Chris /people/faculty/4c9e3afb_cb0b_4746_bbd5_e9e4302ed93a.html
王璐航 /people/faculty/dcb7ab35_a252_4e37_a31d_9ce86229cd85.html
王学新 /people/faculty/cfecc858_9c39_4fff_b056_eaf5999b83e6.html
王艺明 /people/faculty/f4448009_5c81_4725_9798_42ce40412ffb.html
王云 /people/faculty/06fc08d7_5dbd_4d28_8de9_4e7b57c04163.html
萧政 /people/faculty/da83c84c_cb80_431c_97ea_0bb6bd31745f.html
谢沛霖 /people/faculty/a9a0a3b2_99cb_43a6_b683_43861065fe38.html
徐海峰 /people/faculty/faa2370a_0b25_40c5_80f3_75862906b6a3.html
许文超 /people/faculty/dc8a8b18_3b1e_443d_bd49_81d15fd56cde.html
杨荷 /people/faculty/90b06e5e_d49d_43e1_b18c_dd3c855e001c.html
姚加权 /people/faculty/0550534a_6c10_4e82_930c_20cec564e781.html
叶茂亮 /people/faculty/39653e4b_d311_4de5_8974_041b546ad9dc.html
张晶 /people/faculty/9ce540c8_708e_4cf7_8f37_9f00c7edc6a5.html
张烁珣 /people/faculty/17d9773b_6c4d_4314_b35f_ba0d614e2164.html
张宇 /people/faculty/e9bd8598_728f_490d_ab99_87bf6828d544.html
赵宏飙 /people/faculty/81a49a75_1576_4127_983c_3922c8d30e3b.html
赵敏强 /people/faculty/1c12aee5_f441_4978_b4d8_e7e71668e114.html
赵西亮 /people/faculty/86e0f1dd_132f_424c_a14d_ababd7e46463.html
赵燕菁 /people/faculty/66984253_3868_41cc_92da_f17231137282.html
郑鸣 /people/faculty/721ba480_06f3_4c63_8a91_a562159360ff.html
郑挺国 /people/faculty/c5e82359_fd50_4b65_b263_cd974783eb0d.html
钟威 /people/faculty/353d37aa_7187_4486_9712_9f70cf6c4222.html
衷楠 /people/faculty/58717416_c68a_48a5_a60c_8457ddc06e8b.html
周颖刚 /people/faculty/78caf248_ab50_47bc_8f9d_2351a66d1db0.html
邹至庄 /people/faculty/c8d8f928_1fbf_4cdb_86e5_eb93b910e31b.html
'''
好的,到这里一个原型就完成了。这就是一个非常简单的爬虫,总代码不过十几行。复杂到几百几千行的爬虫,都是在这样的一个原型的基础上不断深化、不断完善得到的。
后面的爬虫系列,我们会详细讲解三个部分,然后把每个模块拼起来成一个爬虫。之后我们还会见到更多爬虫方法和爬虫实例。我们下次见~