分布式爬虫系统(基础部分)|青训营笔记

89 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的第 19

一个大数据小白的自学成长之路 浅尝分布式爬虫

其实这篇笔记将近两周前就基本上写好了 奈何咱太拖拉 一直搞到现在才发出来 战术后仰 )。 作为一个大数据领域的萌新 可能没法像技术大牛一样有很多的技术产出 暂且就把我在做项目时的一些心得和所学作为笔记吧

一、项目简介: 简易分布式爬虫系统实现

爬虫系统是搜索引擎领域中很关键,很基础的构件,能够将海量的网页数据下载到本地,进行数据处理后存储和索引以满足不同的使用场景。互联网的网页是海量的,如何能够进行高效的数据爬取是爬虫系统面临的最大挑战之一,一种可行的方式是采用分布式的方式进行数据爬取以提高爬取效率。

二、涉及技术

涉及爬虫和分布式系统两大部分的技术,爬虫部分可能相对简单(仅就完成最基础的功能来说),主要的重点和难点在分布式系统的实现。分布式这部分主要是单机的各个进程之间的通讯即伪分布式和各个设备之间的通讯。

三、实践过程:

先使用python完成基础的爬虫任务(不包括伪分布式)然后在其上用java添加分布式等其他进阶的功能

基础爬虫的主要框架完成包括:

基础功能

数据爬取:进行网页数据并行爬取下载;

数据解析:针对爬取的网页数据进行并行清洗,文本分词等;

    前两部分总结:难度不是很大,正常使用requess和beautifulsoup库去搞定就可以了。并行的部分需要java那边来做。

数据存储模块:将解析好的数据按照一定格式进行存储并构建索引;

数据查询模块:可以进行所需数据的查询;

爬虫进程(或节点)监控:监控爬虫运行时的状态信息;

随机IP代理库:反反爬虫(后续会更新代码及思路)

DNS解析模块:将URL地址转换为网址服务器对应的IP地址(后续会更新代码和思路);

系统完整度:系统功能实现完整,能够正常运转并进行数据爬取:

未来可能存在的难点:接口需要使用新的库,目前还未掌握使用。未能实现并行,不会使用伪分布式即各个进程之间的通讯。

目前的完整代码展示(实现最基础的爬虫功能):

import requests
import re
import os
from bs4 import BeautifulSoup

def download_page(url):
    """
    访问网站的模块,以后需要访问网站的时候都会用到这个函数

    :param:url:str,需要读取的网页地址

    :return:str,下载下来的网页信息
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36'
        }
    data = requests.get(url, headers=headers).content.decode('utf-8')
    # print(data)
    return data


# 解析得到标题与项目地址的列表
def parse_html(url):
    """
    拆分出:每个项目的信息,方便后续解析

    :param:url:需要解析的网址

    :return:ResultSet,项目列表的soup

    """
    html = download_page(url)
    soup = BeautifulSoup(html, features='lxml')
    project_list_soup = soup.find_all('div', {'class': {'work-item grid-item', 'work-item grid-item first'}})
    return project_list_soup



def get_project_title(project_list_soup):
    """
    在项目信息中提取项目的名称,得到一个列表

    :param:project_list_soup:ResultSet,项目列表的soup

    :returns:list[str],项目的名称列表
    """
    project_title = []
    for link in project_list_soup:
        name = link.find('h3', {'class': 'work-item__title fz-20'})
        project_title.append(name.string)
    return project_title


def get_project_link(project_list_soup):
    """
    从项目列表的soup中,解析出每个项目的地址,得到一个列表

    :param:project_list_soup:ResultSet,项目列表的soup

    :return:list[str],记录每个项目页面的地址,将url地址解析成ip地址

    """
    project_link = []
    for link in project_list_soup:
        links = link.find('a')['href']
        # 因为网页地址是一个相对地址,所以需要一个网站的前缀拼接成绝对地址
        links = str(r"https://www.shuishi.com/" + links)
        project_link.append(links)
        newlinks = str(r"www.shuishi.com/" + links)
    return project_link


def parse_sub_html(project_link):
    """
    解析项目子项页面,得到图片地址的列表

    :param:project_link:需要解析的项目地址

    :return:list[str],每张图片的地址的列表
    """
    html = download_page(project_link)
    project_soup = BeautifulSoup(html, features='lxml')
    project_img_links = project_soup.find_all('div', {'class': {'wbanner-item'}})
    # print(project_img_links)
    # print(type(project_img_links))
    project_img_links2 = []
    for l in project_img_links:
        lk = l.find('img')['src']
        lkr = str(r"https://www.shuishi.com/") + lk
        # print(lkr)
        project_img_links2.append(lkr)
    print("此项目大图数量 = " + str(len(project_img_links2)))
    return project_img_links2


def save_img(links, name):
    """
    根据图片地址的列表,设定命名规则,保存图片

    :param links:list,单个项目所需要保存的图片网址列表

    :param name:str,项目的名称,最终存储的文件会加后缀
    """
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'}
    os.makedirs('./SS project img/', exist_ok=True)
    t = 1
    for link in links:
        filename = str('./SS project img/' + str(name) + '%s.jpg' % (repr(t)))
        print("Downloading----", str(filename))
        with open(filename, "wb") as img:
            img.write(requests.get(link, headers=headers).content)
            t += 1


def main():
    # 输入大师的网址,这里的r是转义符
    typeNo = 1
    project_url = r'https://www.shuishi.com/works?type=' + str(typeNo) + r'&pagenum=1'
    name = []
    link = []
    page = 1
    for i in range(page, 10):
        project_url = project_url[0:-1] + str(i)
        print(project_url)
        soup = parse_html(project_url)
        # 得到项目名称以及项目地址
        namei = get_project_title(soup)
        linki = get_project_link(soup)

        print("Name Sub List %d is :" % (i))
        print(namei)
        print("Link Sub List %d is :" % (i))
        print(linki)
        name += namei
        link += linki
    print("共有项目: " + str(len(link)))
    # 进入项目子项地址下载图片
    # 引入项目代号t可以从第t个项目开始下载,以防网络错误
    t = 0
    while t < len(name):
        # 考虑空项目地址引起download函数报错
        if link[t] == "":
            print("Skip it,Because it is null,Hurry coding,,Fix it later")
            t += 1
        else:
            img_list = parse_sub_html(link[t])
            print('Project Name is :', name[t])
            save_name = str("t" + repr(typeNo) + "-" + repr(t + 1) + "-" + name[t])
            save_img(img_list, save_name)
            print('No %s is ok' % repr(t + 1))
            t += 1

if __name__ == '__main__':
    main()