看了《Python项目案例开发从入门到实战》(清华大学出版社 郑秋生 夏敏捷主编)中爬虫应用——校园网搜索引擎,这一章节涉及到的内容有:
- 数据库的基本使用
- 正则表达式
- 中文分词
我详细注释了其中关于校园网搜索引擎的代码,分享给大家:
1 import sys 2 from collections import deque 3 import urllib 4 from urllib import request 5 import re 6 from bs4 import BeautifulSoup 7 import lxml 8 import sqlite3 9 import jieba 10
11 # 要先定义爬虫抓取的第一个网址,这里是是华侨大学的主页
12 url = 'https://www.hqu.edu.cn/index.htm'
13
14 # 待爬取链接的集合,使用广度优先搜索
15 unvisited = deque() 16
17 # 已访问的链接集合
18 visited = set() 19
20 unvisited.append(url) 21
22 # 建立数据库连接,没有则创建数据库
23 conn = sqlite3.connect('viewsdu.db') 24 # 创建游标对象
25 c = conn.cursor() 26 # 在 create table 之前先 drop table 是因为如果你的数据库中已经存在了名叫doc的浏览器了,那么就要再次运行把存在的的doc浏览器删除重建
27 # !!!!如果之前不存在名叫doc的浏览器,那么这一句话要注释掉
28 c.execute('drop table doc') 29 # 创建名叫doc的数据库,包含两个变量,一个是int型的id,一个是text型的link
30 c.execute('create table doc(id int primary key, link text)') 31 # !!!!如果之前不存在名叫word的浏览器,那么这一句话要注释掉
32 c.execute('drop table word') 33 # 创建名叫word的数据库,包含连个变量,一个是varchar(25)型的term,一个是text类型的list
34 c.execute('create table word(term varchar(25) primary key, list text)') 35 # 提交数据库
36 conn.commit() 37 # 关闭数据库
38 conn.close() 39
40 print('************************** 开始爬取 ****************************') 41
42 cnt = 0 43 print('开始..........') 44 # 当还存在带爬取的网页的时候就一直执行循环
45 while unvisited: 46 # 抛出第一个数
47 url = unvisited.popleft() 48 # 已经访问的链接集合添加当前访问链接
49 visited.add(url) 50 cnt += 1
51 print('开始抓取第', cnt, '个链接: ', url) 52
53 # 爬取网页内容
54 try: 55 # 打开网页
56 response = request.urlopen(url) 57 # 读取网页内容并使用 utf-8 进行解码
58 content = response.read().decode('utf-8') 59 except: 60 continue
61
62 # 寻找下一个可爬的链接,因为搜索范围是网站内,所以对链接有格式要求,需根据具体情况而定
63 # 解析网页内容,可能有集中情况,这也是根据这个网站网页的具体情况写的
64 soup = BeautifulSoup(content, 'lxml') 65 # print(soup.prettify())
66 # 找到所有的 target='_blank' 的链接,注意这个要根据爬取的内容进行修改
67 all_a = soup.find_all('a', {'target': '_blank'}) 68 for a in all_a: 69 # print(a.attrs['href'])
70 # 得到href的值
71 x = a.attrs['href'] 72 # 排除开头是http,但不是https://www.hqu.edu.cn
73 if re.match(r'http.+', x): 74 if not re.match(r'http\:\/\/www\.hqu\.edu\.cn\/.+', x): 75 continue
76 # "/info/1046/20314.htm"
77 if re.match(r'\/info\/.+', x): 78 x = 'http://www.hqu.edu.cn' + x 79 # "info/1046/20314.htm"
80 elif re.match(r'info/.+', x): 81 x = 'http://www.hqu.edu.cn/' + x 82 # "../info/1046/20314.htm"
83 elif re.match(r'\.\.\/info/.+', x): 84 x = 'http://www.hqu.edu.cn' + x[2:] 85 # "../../info/1046/20314.htm"
86 elif re.match(r'\.\.\/\.\.\/info/.+', x): 87 x = 'http://www.hqu.edu.cn' + x[5:] 88 if (x not in visited) and (x not in unvisited): 89 print(x) 90 unvisited.append(x) 91 # 下一页<a>
92 a = soup.find('a', {'class': 'Next'}) 93 if a is not None: 94 x = a.attrs['href'] 95 if re.match(r'xwdt\/.+', x): 96 x = 'http://www.hqu.edu.cn/index/' + x 97 else: 98 x = 'http://www.hqu.edu.cn/index/xwdt' + x 99 if (x not in visited) and (x not in unvisited): 100 unvisited.append(x) 101
102 # ************************* 解析网页内容 ************************* #
103 # 得到网页标题
104 title = soup.title 105 # 得到网页内容,注意这个要根据爬取的内容进行修改
106 article = soup.find('div', class_='v_news_content') 107 # 或者使用这个也可以
108 # article = soup.find('div', id='vsb_content')
109 # 作者,注意这个要根据爬取的内容进行修改
110 author = soup.find('span', class_='arti_publisher') 111 # 发布时间,注意这个要根据爬取的内容进行修改
112 time = soup.find('span', class_='arti_update') 113 # print('title : \n', title)
114 # print('article : \n', article)
115 # print('author : \n', author)
116 # print('time : \n', time)
117
118 if title is None and article is None and author is None: 119 print('无内容的页面。') 120 continue
121
122 elif article is None and author is None: 123 print('只有标题。') 124 title = title.text 125 title = ''.join(title.split()) 126 article = ''
127 author = ''
128
129 elif article is None: 130 print('有标题有作者,缺失内容') 131 title = title.text 132 title = ''.join(title.split()) 133 article = ''
134 author = author.get_text("", strip=True) 135 author = ''.join(author.split()) 136
137 elif author is None: 138 print('有标题有内容,缺失作者') 139 title = title.text 140 title = ''.join(title.split()) 141 article = article.get_text("", strip=True) 142 article = ''.join(article.split()) 143 author = ''
144 else: 145 # 得到标签中文字内容
146 title = title.text 147 # 去除空格
148 title = ''.join(title.split()) 149 # 得到变迁中文字内容,strip=True表示去除空白行
150 article = article.get_text("", strip=True) 151 article = ''.join(article.split()) 152 author = author.get_text("", strip=True) 153 author = ''.join(author.split()) 154
155 print('网页标题:', title) 156
157 # 对title,article,author内容的词进行结巴分词,使用的是搜索引擎模式的cut_for_search
158 seglist = [] 159 for i in [title, article, author]: 160 seggen = jieba.cut_for_search(i) 161 seglist.extend(seggen) 162
163 # 数据传输
164 conn = sqlite3.connect("viewsdu.db") 165 c = conn.cursor() 166 # 在名为doc的数据库中插入行的一行
167 c.execute('insert into doc values (?,?)', (cnt, url)) 168 # 对每个分出的词语建立倒排词表
169 for word in seglist: 170 # 检验看看这个词语是否已存在于数据库
171 c.execute('select list from word where term=?', (word,)) 172 result = c.fetchall() 173 # 如果不存在
174 if len(result) == 0: 175 docliststr = str(cnt) 176 # 在word数据库中插入新的行值
177 c.execute('insert into word values (?, ?)', (word, docliststr)) 178 # 如果已存在
179 else: 180 # 提取当前结果返回的内容,返回的就是如 ‘19 19’
181 docliststr = result[0][0] 182 # 加上新得到的内容,返回的内容如 ‘19 19 20’
183 docliststr += ' ' + str(cnt) 184 # 更新word数据库中term对应的值
185 c.execute('update word set list=? where term=?', (docliststr, word)) 186 conn.commit() 187 conn.close() 188 print('词表建立完毕!!!')
这里需要注意:关于前端网页中定义的那些搜索词,比如:{'target': '_blank'}, class_='v_news_content', class_='arti_publisher', class_='arti_update' 这些内容都是根据爬取的网页具体情况决定,要学会灵活变通。我这些名字来源主要因为如下:
class_='arti_update' 来源:
class_='v_news_content' 来源:
class_='arti_publisher'来源:
类似这样得到这些值。
最终的结果是返回得到一个词表数据库,如下图所示:
viewsdu.db 数据库:
doc 浏览器:
word 浏览器: