可断点爬虫实现(二)

本文最后更新于:1 小时前

相信看到这里的同学们已经看过我写的第一篇文章了,那接下来这一篇我们主要来讲讲如何将上一篇的代码组合起来,能够使我们的代码最优化

1. 算法

讲到这里呢,我们先来谈谈两种算法。
相信学过数据结构的人都知到在树的遍历里面有两种很经典的搜索算法,他们分别是 深度优先搜索广度优先搜索,如果说你学过这一部分的知识的话,那么就可以跳过这一部分内容了,如果说没有学过,那么不要着急,此处会慢慢给大家分析分析。

  • 深度优先搜索(DFS)

    深度优先搜索,根据它的字面意思可以了解到它是会优先不断地向下一层去搜索,直到这棵树再没有子节点,程序就会返回上一个层去搜索另一个节点。它的运行很类似于树的前序遍历。

  • 广度优先搜索(BFS)

    广度优先搜索每次运行都会先遍历完当前一层的所有节点,然后再去遍历下一层,它的运行过程很类似于树的层序遍历。

  • 代码实现

// C++代码实现模板
#include <bits/stdc++.h>
using namespace std;

map<int, vector<int>> L;

// 深度优先搜索实现
void dfs(int node)
{
    for (int item : L[node])
    {
        cout << item << endl;
        if (!L[item].empty())
        {
            dfs(item);
        }
    }
}

// 广度优先搜索实现
void bfs()
{
    queue<int> ll;
    ll.push(1);
    while (!ll.empty())
    {
        int temp = ll.front();
        ll.pop();
        for (int item : L[temp])
        {
            ll.push(item);
            cout << item << endl;
        }
    }
}

int main()
{
    L[1] = {2, 3, 4};
    L[2] = {5, 6, 7};
    L[3] = {8, 9, 10};
    L[4] = {11, 12, 13};
    dfs(1);
    cout << "---------------" << endl;
    bfs();
}
# python 代码模板实现
L = [
    [],
    [2, 3, 4],
    [5, 6, 7],
    [8, 9, 10],
    [11, 12, 13]
]

# 深度优先搜索
def dfs(node):
    for item in L[node]:
        print(item)
        if item < len(L):
            dfs(item)

# 广度优先搜索
def bfs():
    ll = []
    ll.append(1)
    while ll:
        temp = ll[0]
        ll.pop(0)
        for item in L[temp]:
            if item < len(L):
                ll.append(item)
            print(item)


if __name__ == '__main__':
    dfs(1)
    print('--------------------')
    bfs()

以上两种代码实现效果一样

2. 代码实现

既然了解了深度优先搜索和广度优先搜索,那么接下来我们就聊一聊我们代码的实现

首先我先贴出我刚开始写的代码(通过深度优先搜索实现)

def get_data_by_dfs(url, deep, father_id=0):
    global book
    message = f'''
        ---------------------开始爬取--------------------
        当前爬取的节点:\t{father_id}
        当前爬取的深度:\t{deep}
        -------------------------------------------------
        '''
    print(message)

    temp_url = url.split('/')[:-1]      # 爬取地址处理,方便下一层地址的拼接

    if deep == 1:
        temp_data = fetch_province_list(url=url)
        for item in temp_data:
            # print(item)
            book["province"].append(((item['id'], item['name'], item['url'])))

            cur_url = temp_url + [item['url']]
            cur_url = "/".join(cur_url)     # url地址拼接
            get_data_by_dfs(cur_url, deep+1, father_id=item['id'])
    elif 1 < deep <= 4:
        temp_data = fetch_district_list(url=url, deep=deep)
        for item in temp_data:
            if deep == 2:
                print(item)
                book["municipality"].append(
                    ((item['id'], item['name'], item['url'], father_id)))
                # time.sleep(60)
            elif deep == 3:
                book["district"].append(
                    ((item['id'], item['name'], item['url'], father_id)))
                # time.sleep(30)
            elif deep == 4:
                book["township"].append(
                    ((item['id'], item['name'], item['url'], father_id)))
                # time.sleep(10)

            cur_url = temp_url + [item['url']]
            cur_url = "/".join(cur_url)     # url地址拼接
            get_data_by_dfs(cur_url, deep+1, father_id=item['id'])

    elif deep == 5:
        temp_data = fetch_village_list(url=url)
        for item in temp_data:
            # print(item)
            book["village"].append(
                ((item['id'], item['city_id'], item['name'], father_id)))
    message = f'''
        ---------------------爬取结束---------------------
        当前爬取的节点:\t{father_id}
        当前爬取的深度:\t{deep}
        -------------------------------------------------
        '''
    print(message)

通过运行我发现在爬取大量数据的时候程序会出现各种各样的错误,如果我们在程序跑了几个小时后突然因为异常而退出,那么我们下次爬虫的时候就要从头开始,这会大量的消耗我们的时间。为了解决这个问题,考虑能不能将我们运行时产生的数据储存起来,然后再下次运行时调用上次运行结束时的数据。但是在这个递归的程序之中不太容易储存运行数据,所以就有了后面的版本,通过广搜的方法来运行程序。

def get_data_by_bfs():
    global book
    queue = []
    file = open('data.json', 'r')
    text = file.read()
    file.close()
    data = json.loads(text)
    for item in data['data']:
        queue.append(item)
    while queue:
        temp_data = queue[0]
        queue.pop(0)
        print(temp_data)
        father_id = temp_data['father_id']
        deep = temp_data['deep']
        url = temp_data['url']
        temp_url = url.split('/')[:-1]
        message = f'''
            ---------------------开始爬取--------------------
            当前爬取的节点:\t{father_id}
            当前爬取的地址:\t{url}
            当前爬取的深度:\t{deep}
            -------------------------------------------------
            '''
        print(message)
        if deep == 1:
            address = fetch_province_list(url=url)
            for item in address:
                print(item)
                book["province"].append(
                    ((item['id'], item['name'], item['url'])))
                cur_url = temp_url + [item['url']]
                cur_url = "/".join(cur_url)
                queue.append(
                    {"father_id": item['id'], "deep": deep+1, "url": cur_url})
        elif 2 <= deep <= 4:
            address = fetch_district_list(url=url, deep=deep)
            for item in address:
                if deep == 2:
                    book["municipality"].append(
                        ((item['id'], item['name'], item['url'], father_id)))
                elif deep == 3:
                    book["district"].append(
                        ((item['id'], item['name'], item['url'], father_id)))
                elif deep == 4:
                    book["township"].append(
                        ((item['id'], item['name'], item['url'], father_id)))
                cur_url = temp_url + [item['url']]
                cur_url = "/".join(cur_url)
                queue.append({"father_id": item['id'],
                              "deep": deep+1, "url": cur_url})
                # print(item)
                message = f'''
                    ---------------------爬取中--------------------
                    当前爬取的节点:\t{item['id']}
                    当前爬取的名称:\t{item['name']}
                    当前爬取的深度:\t{deep}
                    -------------------------------------------------
                    '''
                print(message)
        elif deep == 5:
            address = fetch_village_list(url=url)
            for item in address:
                print(item)
                book["village"].append(
                    ((item['id'], item['city_id'], item['name'], father_id)))
        message = f'''
            ---------------------爬取结束--------------------
            当前爬取的节点:\t{father_id}
            当前爬取的地址:\t{url}
            当前爬取的深度:\t{deep}
            -------------------------------------------------
            '''
        print(message)
        book.save("address.xlsx")
        content = json.dumps({"data": queue})
        file = open('data.json', 'w')
        file.write(content)
        file.close()

相信有过爬虫经历的同学都知道,爬虫爬的多了会被禁用,导致在一段时间无法进入到那个网站,那么在下一篇我们来聊聊我们如何优化我们的爬虫代码