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(加入数据库)

代码目录结构:

python 爬虫 引用站点策略 python爬虫引擎_sqlite

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、知识补充

  1. Urllib库的基本使用 ;
from urllib import request (负责打开浏览url内的html 文本)
response = urlopen(url)

soup = BeautifulSoup(response.read(), 'html.parser')
  1. BeautifulSoup: 是一种非常优雅的专门用于进行HTML/XML数据解析的一种描述语言,可以很好的分析和筛选HTML/XML这样的标记文档中的指定规则数据
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.read(), 'html.parser')
# 获取页面中所有的超链接
ls_a = soup.find_all('a')


  1. 正则表达式
import re
res = re.match('^http[s]{0,1}://', a_url)
  1. if name == ‘main’ 的作用
通俗的理解__name__ == '__main__':假如你叫小明.py,在朋友眼中,你是小明(__name__ == '小明');在你自己眼中,你是你自己(__name__ == '__main__')。

if __name__ == '__main__'的意思是:当.py文件被直接运行时,if __name__ == '__main__'之下的代码块将被运行;当.py文件以模块形式被导入时,if __name__ == '__main__'之下的代码块不被运行。


  1. 数据库sqlite3
    http://www.runoob.com/sqlite/sqlite-python.html