网站爬虫(全站爬取)案例以及原理 适合新手

一、网站介绍

本次案例选用的网站是由崔先生(崔庆才)所维护的网站,今天的案例是也是网站中的一部分。

https://ssr1.scrape.center/

二、基础模块

本次用到的模块有:

1、json

本章会用到json.dump()方法,将数据保存成文本格式,用于数据的保存

2、requests

本章用到的数据请求方法主要采用requests.get()方法来请求

3、multiprocessing

创立多线程池,用于加快爬虫速度

4、urllib

基本不用urllib请求数据,太麻烦。但urljoin来拼接url还是不错的

5、os

系统库,用于创建文件夹

三、网站解析

本次的网站是一个非常基础的没有加密的网站,主要看一下它的数据位置以及翻页规则。

首先我们在电影信息块中可以看到蕴含着一个url后缀

点进电影详情后发现电影详情的url是原url与这个url后缀拼接而成的。

而它的翻页规则也很简单,是在原url的基础上加/page/页数

至此我们完成了对网站结构的解析

四、爬取准备

了解网站的结构后,接下来要进行数据爬取,首先是先得到不同页数的url的文本内容

total_page = 10

#如果网页请求成功则范围网页请求内容
def scrape_page(url):
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.text
    except:
        raise

#得到不同页的url
def scrape_index(page):
    page = str(page)
    index_url = base_url+"page/" + page
    return scrape_page(index_url)

for page in range(1,total_page + 1):
    index_html = scrape_index(page)

得到不同页数的url之后,我们要得到每一页电影元素的详情页url后缀,并生成new_url返回

def parse_index(html):
    detail_url = re.findall(r'<a data-v-7f856186="" href="(.*?)" class', html, re.S)
    for item in detail_url:
        new_url = urljoin(base_url,item)
        yield new_url

deatil_urls = parse_index(index_html)

通过

for url in detail_urls:

        print(url)

我们得到得数据应该是这样的

每一个url都是一个完整的电影详情页。此时已经完成了得到所有要解析网站的url了

接下来将进行数据解析的部分。网站很简单想怎么解析就怎么解析,但本次我采用与书上相同的方法进行了正则解析。并返回了一个json格式

def parse_detail(html):
    img_pattern = re.compile('class="item.*?src="(.*?)".*?class="cover">',re.S)
    img = re.search(img_pattern,html).group(1) if re.search(img_pattern,html) else None
    name_pattern = re.compile('<h2 data-v-63.*?>(.*?)</h2>',re.S)
    name = re.search(name_pattern,html).group(1).strip() if re.search(name_pattern,html) else None
    categories_pattern = re.compile('class="el-button cat.*? <span>(.*?)</span>',re.S)
    categories = re.findall(categories_pattern,html) if re.search(categories_pattern,html) else None
    time_pattern = re.compile('(\d{4}-\d{2}-\d{2}) 上映')
    time = re.search(time_pattern,html).group(1) if re.search(time_pattern,html) else None
    score_pattern = re.compile('class="score.*?m">(.*?)</p>',re.S)
    score = re.search(score_pattern,html).group(1).strip() if re.search(score_pattern,html) else None
    drama_pattern = re.compile('<p data-v-63864230="">(.*?)</p>',re.S)
    drama = re.search(drama_pattern,html).group(1).strip() if re.search(drama_pattern,html) else None
    return {'img': img,
            'name': name,
            'categories': categories,
            'time': time,
            'score': score,
            'drama': drama
            }

接下来在将返回下来的数据请求存入到本地文件夹之中,先确定一个目标文件地址,之后通过os模块检查是否存在这个文件,若不存在则新建一个

result_dir = 'your taget dir'
exists(result_dir) or makedirs(result_dir)

再将数据存入,data_path为我们要存入的文件地址,json.dump()则是我们想以什么样的方式存入数据。

def sava_data(data):
    name = data.get('name')
    data_path = f'{result_dir}/{name}.json'
    json.dump(data,open(data_path,'w',encoding='utf-8'),
              ensure_ascii=False,indent=2)

至此我们就完成了本次网站的数据解析。

接下来是完整代码:

import json
import requests, re
import multiprocessing
from urllib.parse import urljoin
from os import makedirs
from os.path import exists

result_dir = 'D:\桌面/movie'
exists(result_dir) or makedirs(result_dir)
base_url = 'https://ssr1.scrape.center/'
total_page = 10

def scrape_page(url):
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.text
    except:
        raise
def scrape_index(page):
    page = str(page)
    index_url = base_url+"page/" + page
    return scrape_page(index_url)


def parse_index(html):
    detail_url = re.findall(r'<a data-v-7f856186="" href="(.*?)" class', html, re.S)
    for item in detail_url:
        new_url = urljoin(base_url,item)
        yield new_url


def scrapy_detail(url):
    return scrape_page(url)

def parse_detail(html):
    img_pattern = re.compile('class="item.*?src="(.*?)".*?class="cover">',re.S)
    img = re.search(img_pattern,html).group(1) if re.search(img_pattern,html) else None
    name_pattern = re.compile('<h2 data-v-63.*?>(.*?)</h2>',re.S)
    name = re.search(name_pattern,html).group(1).strip() if re.search(name_pattern,html) else None
    categories_pattern = re.compile('class="el-button cat.*? <span>(.*?)</span>',re.S)
    categories = re.findall(categories_pattern,html) if re.search(categories_pattern,html) else None
    time_pattern = re.compile('(\d{4}-\d{2}-\d{2}) 上映')
    time = re.search(time_pattern,html).group(1) if re.search(time_pattern,html) else None
    score_pattern = re.compile('class="score.*?m">(.*?)</p>',re.S)
    score = re.search(score_pattern,html).group(1).strip() if re.search(score_pattern,html) else None
    drama_pattern = re.compile('<p data-v-63864230="">(.*?)</p>',re.S)
    drama = re.search(drama_pattern,html).group(1).strip() if re.search(drama_pattern,html) else None
    return {'img': img,
            'name': name,
            'categories': categories,
            'time': time,
            'score': score,
            'drama': drama
            }

def sava_data(data):
    name = data.get('name')
    data_path = f'{result_dir}/{name}.json'
    json.dump(data,open(data_path,'w',encoding='utf-8'),
              ensure_ascii=False,indent=2)

def main():
    for page in range(1,total_page + 1):
        index_html = scrape_index(page)
        deatil_urls = parse_index(index_html)
        for detail_url in deatil_urls:
            print(detail_url)
            detail_html = scrapy_detail(detail_url)
            data = parse_detail(detail_html)
            sava_data(data)

if __name__ == '__main__':
    main()

也有小伙伴反应爬取速度太慢怎么办,那么此刻加入一个多进程爬取就可以大大提升速度。将main函数改为muti_main()  同时启动也修改一下即刻。我试了一下,不采用多进程完成数据大概要45s,开启多进程仅需13s(当然跟网速也有很大的关系)。

def muti_main(page):
    index_html = scrape_index(page)
    deatil_urls = parse_index(index_html)
    for detail_url in deatil_urls:
        detail_html = scrapy_detail(detail_url)
        data = parse_detail(detail_html)
        sava_data(data)

if __name__ == '__main__':
    pool = multiprocessing.Pool()
    pages = range(1,total_page + 1)
    pool.map(muti_main,pages)
    pool.close()
    pool.join()

爬取结果如下:

至此我们就完成了一个入门基本的全站爬虫案例,接下来还会有更难的案例,让我们一起进步吧~

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值