1. 项目简介
设计一个 Web 服务器 server.py,它读取 students.txt 文件中的学生数据,以表格的形式呈现在网页上,其中 students.txt 的格式如下:
No,Name,Gender,Age
1001,张三,男,20
1002,李四,女,19
1003,王五,男,21
设计一个客户端的爬虫程序client.py,它从这个网页上爬行学生的这些信息,存储到数据库中。学生数据库可以使用 Sqllite 数据库 students.db。
2. 服务器程序
服务器程序首先读取同一个目录下的 students.txt 文件,然后组成一张<table>的HTML表格用网页的形式呈现,效果如下图:
程序先检查是否有 students.txt 文件存在,如果文件存在就打开读取,读出一行的数据使用逗号分开的,因此使用 split(",") 函数拆分,然把一行数据组成在<tr>...</tr>的行中,把每个数据组织在<td>...</td>的单元格中
服务器程序server.py 如下:
importflask
importos
app=flask.Flask(__name__)
@app.route("/")
defshow():
ifos.path.exists("students.txt"):
st="<h3>学生信息表</h3>"
st=st+"<table border='1' width='300'>"
fobj=open("students.txt", "rt", encoding="utf-8")
whileTrue:
s=fobj.readline().strip("\n") # 读取一行,去除行尾部"\n"换行符号
ifs=="": # 如果读到文件尾部就退出
break
s=s.split(",") # 按逗号拆分开
st=st+"<tr>"
foriinrange(len(s)): # 把各个数据组织在<td>...</td>的单元格中
st=st+"<td>"+s[i] +"</td>"
st=st+"</tr>" # 完成一行
fobj.close()
st=st+"</table>"
returnst
if__name__=="__main__":
app.run()
运行服务器程序,默认网址为 http://127.0.0.1:5000/ 。
3. 客户端程序
客户端程序访问网址 http://127.0.0.1:5000/,从中下载其 HTML 网页,这个网页的结果如下:
<h3>学生信息表</h3>
<tableborder='1'width='300'>
<tr>
<td>No</td>
<td>Name</td>
<td>Gender</td>
<td>Age</td>
</tr>
<tr>
<td>1001</td>
<td>张三</td>
<td>男</td>
<td>20</td>
</tr>
<tr>
<td>1002</td>
<td>李四</td>
<td>女</td>
<td>19</td>
</tr>
<tr>
<td>1003</td>
<td>王五</td>
<td>男</td>
<td>21</td>
</tr>
</table>
程序要从这个 HTML 网页爬取数据,只要分解出第一行:
<tr><td>No</td><td>Name</td><td>Gender</td><td>Age</td></tr>
再次分解这一行的<td>...</td>数据,就知道这个表有哪些标题字段,这个表目前有 No、Name、Gender、Age 字段。
接下来再次分解出下一行的<tr>...</tr>数据:
<tr><td>1001</td><td>张三</td><td>男</td><td>20</td></tr>
再次分解这一行的<td>...</td>数据,得到 No、Name、Gender、Age 的数据依次是 "1001"、"张三"、"男"、"20",把这一行的数据写入对应的数据库即可。
要分解出<tr>...</tr>只要使用 r"<tr>" 与 r"/tr" 的正则表达式即可,先用 r"<tr>" 匹配 HTML 代码,得到第一个 <tr> 的位置,再使用 r"</tr>" 匹配 HTML 字符串,得到第一个 </tr> 的位置,取出 <tr>...</tr> 的数据部分,再次使用 r"<td>" 与 r"</td>" 的正则表达式分解<td>...</td> 的数据。
客户端程序client.py 如下:
importurllib.request
importre
importsqlite3
# 在html中查找学生信息
defsearchWeb(html):
rows= []
# 查询第一个<tr>...</tr>行
m=re.search(r"<tr>", html)
n=re.search(r"</tr>", html)
ifmisnotNoneandnisnotNone:
# 跳过第一行的标题
html=html[n.end():]
# 查询第二行开始的数据部分
m=re.search(r"<tr>", html)
n=re.search(r"</tr>", html)
whilemisnotNoneandnisnotNone:
row= []
start=m.end() # start是<tr>的结束位置
end=n.start() # end是</tr>的开始位置
t=html[start:end] # t 是<tr>...</tr>包含的字符串
html=html[n.end():] # 是剩余的html
# 查询第一组<td>...</td>
a=re.search(r"<td>", t)
b=re.search(r"</td>", t)
whileaisnotNoneandbisnotNone:
start=a.end() # start是<td>的结束位置
end=b.start() # end是</td>的开始位置
row.append(t[start:end]) # 找到一组<td>...</td>的数据
t=t[b.end():] # 本行剩余的部分
a=re.search(r"<td>", t)
b=re.search(r"</td>", t)
# 增加一行数据
rows.append(row)
# 继续查找下一行<tr>...</tr>
m=re.search(r"<tr>", html)
n=re.search(r"</tr>", html)
returnrows
# 保存学生信息到数据库
defsaveDB(rows):
iflen(rows) ==0: # 没有数据就返回
return
try:
con=sqlite3.connect("students.db")
cursor=con.cursor()
try:
cursor.execute("drop table students") # 如果有students表就删除
except:
pass
try:
# 建立新的students表
sql="create table students (No varchar(128) primary key,Name varchar(128),Gender varchar(128),Age int)"
cursor.execute(sql)
except :
pass
forrowinrows:
iflen(row) ==4:
# 插入一条记录
sql="insert into students (No,Name,Gender,Age) values (?,?,?,?)"
try:
No, Name, Gender, Age=row[0], row[1], row[2], int(row[3])
cursor.execute(sql, (No, Name, Gender, Age))
exceptExceptionaserr:
print(err)
con.commit() # 数据库提交保存
con.close()
exceptExceptionaserr:
print(err)
# 显示查找的信息
defshowWeb(rows):
print("显示来自Web的数据...")
forrowinrows:
print(row)
# 显示数据库的数据
defshowDB():
print("显示来自DB的数据...")
try:
con=sqlite3.connect("students.db")
cursor=con.cursor()
cursor.execute("select * from students") # 查询数据库记录
rows=cursor.fetchall()
# 显示每条记录
forrowinrows:
print(row)
con.close()
exceptExceptionaserr:
print(err)
try:
url="http://127.0.0.1:5000"
resp=urllib.request.urlopen(url) # 访问这个网址获取 html
data=resp.read()
html=data.decode()
rows=searchWeb(html) # 在html中查找学生信息
showWeb(rows) # 显示查找的信息
saveDB(rows) # 保存学生信息到数据库
showDB() # 显示数据库的数据
exceptExceptionaserr:
print(err)
程序执行后结果显示客户端从服务器的网页上爬取了学生信息并保存到来数据库: