1、代码1.1版本
'''
简单的搜索引擎
1.
起点:
url: http://site.baidu.com
2.
起点页面:页面的内容;页面中的超链接
3.
超链接的各个页面:页面的内容;页面中的超链接
4.
算法:深度优先(直接递归)/广度优先(队列)
'''
from urllib.request import urlopen, Request
from bs4 import BeautifulSoup
# 引入正则表达式
import re
# 抓取的起点
# url = 'http://site.baidu.com/'
# 待抓取的链接队列:【首先放入抓取起点:首页面】
urls = ['http://site.baidu.com/']
# 已经抓取过的url队列
old_urls = []
# 伪装浏览器的头
headers = {
'User-Agent':''
}
def spider_url(url):
# 保存该url到已抓取队列
old_urls.append(url)
# 构造请求对象,伪装头信息
req = Request(url, headers = headers)
# 打开请求对象
response = urlopen(req)
soup = BeautifulSoup(response.read(), 'html.parser')
# 处理页面内容
# 1.页面标题
m_title = soup.title
if m_title:
title = soup.title.get_text()
print(title)
# 2. 页面中的关键字
m_keywords = soup.select('meta[name="keywords"]')
if m_keywords:
keywords = m_keywords.attrs.get('content')
print(keywords)
# 3. 页面中的h1-h6标签
m_h1s = soup.find_all('h1')
# 4. 页面中所有的P标签
m_ps = soup.find_all('p')
# 抓取3,4中的字符串
context = ''
for h1 in m_h1s:
context += h1.get_text()
for p in m_ps:
context += p.get_text()
print(context)
# 获取页面中所有的超链接
ls_a = soup.find_all('a')
# 抓取超链接列表中的每一个页面
for a in ls_a:
a_url = a.attrs.get('href')
# a_url不一定是一个有效的超链接
if a_url == None:
print('无效的链接')
continue
# 过滤非常规的超链接,只抓取http://获取https://开始
res = re.match('^http[s]{0,1}://', a_url)
if res == None:
print('非正常链接')
continue
# 输入有效的url
print(a_url)
# 1.递归抓取有效url:深度优先
# spider_url(a_url)
# 2.队列:广度优先
# urls.append(a_url)
# 定义一个函数优化抓取过程:抓过的不抓了或者已经在待抓取队列中了
append_url(a_url)
def append_url(a_url):
# 加入到队列中时,判断队列中是否已经有url
if a_url in urls:
print('队列中已经存在该url')
return
# url已经抓取过了
if a_url in old_urls:
print('已经抓取过该url')
return
# 将url加入到抓取队列
urls.append(a_url)
if __name__ == '__main__':
while True:
# 从队列中取出一个链接,抓取该链接的页面
url = urls.pop(0)
spider_url(url)
2、代码2.0(加入数据库)
代码目录结构:
spider.py
'''
简单的搜索引擎
1.
起点:
url: http://site.baidu.com
2.
起点页面:页面的内容;页面中的超链接
3.
超链接的各个页面:页面的内容;页面中的超链接
4.
算法:深度优先(直接递归)/广度优先(队列)
'''
from urllib.request import urlopen, Request
from bs4 import BeautifulSoup
# 引入正则表达式
import re
from db_util import create_table, save, search_by_url, search_url, save_url, get_url, delete_url
# 抓取的起点
# url = 'http://site.baidu.com/'
# 待抓取的链接队列:【首先放入抓取起点:首页面】
urls = ['http://site.baidu.com/']
# 已经抓取过的url队列
old_urls = []
# 伪装浏览器的头
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
def spider_url(url):
# 字典
site = {}
site['url'] = url
# 保存该url到已抓取队列
# old_urls.append(url)
# 构造请求对象,伪装头信息
req = Request(url, headers = headers)
# 打开请求对象
response = urlopen(req)
soup = BeautifulSoup(response.read(), 'html.parser')
# 处理页面内容
# 1.页面标题
m_title = soup.title
if m_title:
title = soup.title.get_text()
print(title)
site['title'] = title
# 2. 页面中的关键字
m_keywords = soup.select('meta[name="keywords"]')
if m_keywords:
keywords = m_keywords.attrs.get('content')
print(keywords)
site['keywords'] = keywords
# 3. 页面中的h1-h6标签
m_h1s = soup.find_all('h1')
# 4. 页面中所有的P标签
m_ps = soup.find_all('p')
# 抓取3,4中的字符串
context = ''
for h1 in m_h1s:
context += h1.get_text()
for p in m_ps:
context += p.get_text()
print(context)
site['context'] = context
# 保存到数据库中
save(site)
# 获取页面中所有的超链接
ls_a = soup.find_all('a')
# 抓取超链接列表中的每一个页面
for a in ls_a:
a_url = a.attrs.get('href')
# a_url不一定是一个有效的超链接
if a_url == None:
print('无效的链接')
continue
# 过滤非常规的超链接,只抓取http://获取https://开始
res = re.match('^http[s]{0,1}://', a_url)
if res == None:
print('非正常链接')
continue
# 输入有效的url
print(a_url)
# 1.递归抓取有效url:深度优先
# spider_url(a_url)
# 2.队列:广度优先
# urls.append(a_url)
# 定义一个函数优化抓取过程:抓过的不抓了或者已经在待抓取队列中了
append_url(a_url)
def append_url(a_url):
# 加入到队列中时,判断队列中是否已经有url
if search_url(a_url):
print('队列中已经存在该url')
return
# url已经抓取过了
if search_by_url(a_url):
print('已经抓取过该url')
return
# 将url加入到抓取队列
# urls.append(a_url)
save_url(a_url)
def spider(count):
# 创建数据库
create_table()
# 控制抓取数量
while True:
# 从队列中取出一个链接,抓取该链接的页面
# url = urls.pop(0)
url = get_url()
delete_url(url)
spider_url(url)
count -= 1
if count == 0:
break
main.py
from spider import spider
from db_util import search_by_title
def search():
while True:
key = input('请输入查询的title关键字:')
if len(key) == 0:
print('关键字不能为空!')
continue
else:
# 执行查询操作
ls = search_by_title(key)
# 显示查询结果
for site in ls:
print(site)
input('输入任意键继续')
# 跳出循环
break
if __name__ == '__main__':
while True:
# 提供爬取、查询功能
print('简单的搜索引擎')
print('1. 抓取数据')
print('2. 查询数据')
print('3. 退出')
# 获取用户输入。input得到的是字符串
cmd = input('请输入功能选项代号:')
if cmd == '1':
count = input('请输入抓取数量:')
spider(int(count))
elif cmd == '2':
print('查询数据')
search()
elif cmd == '3':
print('退出程序')
break
else:
print('输入无效,请重新输入')
continue
db_util.py
'''
db_util
1. 创建数据库和表
'''
import sqlite3
import os
db_file = 'site.db'
# 创建数据表
def create_table():
if os.path.exists(db_file):
return
# 链接数据库
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
# 已经抓取过的url表
cursor.execute('''
create table site(
id integer primary key autoincrement,
url text,
title text,
keywords text,
context text
)
''')
# 待抓取的url表
cursor.execute('''
create table url(
id integer primary key autoincrement,
url text
)
''')
conn.commit()
conn.close()
# 保存抓取信息
# 保存数据到数据库
def save(site):
# 创建一个表示数据库的连接对象
conn = sqlite3.connect(db_file)
# 创建光标对象
cursor = conn.cursor()
# 该例程执行一个 SQL 语句。该 SQL 语句可以被参数化(即使用占位符代替 SQL 文本)。
cursor.execute('''
insert into site(url, title, keywords, context)
values(?, ?, ?, ?)
''', (site.get('url'), site.get('title'), site.get('keywords'), site.get('context')))
# 法提交当前的事务。如果您未调用该方法,那么自您上一次调用 commit() 以来所做的任何动作对其他数据库连接来说是不可见的。
conn.commit()
# 关闭数据库连接。请注意,这不会自动调用 commit()。如果您之前未调用 commit() 方法,就直接关闭数据库连接,您所做的所有更改将全部丢失!
conn.close()
# 搜索数据
def search_by_title(key):
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
res = cursor.execute('''
select * form site
where title like ?
''', ('%'+key+'%', ))
# 列表
ls = []
for row in res:
# 字典
site = {}
site['id'] = row[0]
site['url'] = row[1]
site['title'] = row[2]
site['keywords'] = row[3]
site['context'] = row[4]
ls.append(site)
conn.close()
return ls
# 判断URL是否存在已抓取过的表中
def search_by_url(url):
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
res = cursor.execute('''
select * from site
where url = ?
''', (url, ))
count = len(cursor.fetchall())
conn.close()
# 此url已经抓取过
if count > 0:
return True
else:
return False
# 保存待抓取的url
def save_url(url):
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
cursor.execute('''
insert into url(url)
values(?)
''', (url, ))
conn.commit()
conn.close()
# 从待查表中查询一条url
def get_url():
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
url = None
res = cursor.execute('''
select * from url
''')
# 只需取一个url
for row in res:
url = row[1]
break
conn.close()
return url
# 在待查表url中删除刚刚查过的url
def delete_url(url):
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
cursor.execute('''
delete from url
where url = ?
''', (url,))
conn.commit()
conn.close()
# 判断一个url在待抓取表中是否存在
def search_url(url):
conn = sqlite3.connect(db_file)
cursor = conn.cursor()
cursor.execute('''
select * from url
where url = ?
''', (url, ))
count = len(cursor.fetchall())
conn.close()
if count > 0:
return True
else:
return False
3、知识补充
- Urllib库的基本使用 ;
from urllib import request (负责打开浏览url内的html 文本)
response = urlopen(url)
soup = BeautifulSoup(response.read(), 'html.parser')
- BeautifulSoup: 是一种非常优雅的专门用于进行HTML/XML数据解析的一种描述语言,可以很好的分析和筛选HTML/XML这样的标记文档中的指定规则数据
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.read(), 'html.parser')
# 获取页面中所有的超链接
ls_a = soup.find_all('a')
- 正则表达式
import re
res = re.match('^http[s]{0,1}://', a_url)
- if name == ‘main’ 的作用
通俗的理解__name__ == '__main__':假如你叫小明.py,在朋友眼中,你是小明(__name__ == '小明');在你自己眼中,你是你自己(__name__ == '__main__')。
if __name__ == '__main__'的意思是:当.py文件被直接运行时,if __name__ == '__main__'之下的代码块将被运行;当.py文件以模块形式被导入时,if __name__ == '__main__'之下的代码块不被运行。
- 数据库sqlite3
http://www.runoob.com/sqlite/sqlite-python.html