BUG-Fly BUG-Fly
  • 首页
  • BUG-EXP
  • 编程开发
  • 电脑评测
  • 生活分享
  • 友情链接
  • Fly全站协议声明
首页 › Python › 某软件资源站采集

某软件资源站采集

BUG-Fly
5 年前⦁置顶

昨天一位好友,找我帮忙写一个采集某资源站的程序。项目需求如下:

项目需求

  • 采集软件详情页
  • 采集软件的名称
  • 采集软件的分类标签
  • 采集软件的图片
  • 采集软件的发布时间
  • 采集软件的正文描述

项目开发周期

半天

开发思路

首先,先来看一下网页的大概布局:

某软件资源站采集-BUG-Fly

在主导航栏有软件的分类,而在首页的内容中,软件是混合在一起展示的。考虑到需求采集软件的分类标签,何不根据分类进行采集呢。

来到软件详情页:

某软件资源站采集-BUG-Fly

现在就看这些数据是怎么来的了?!

打开开发者工具,在异步勾选:

某软件资源站采集-BUG-Fly

但是两个异步接口没有相关的信息返回,好吧貌似不能直接异步爬去,这给开发增加了一丝丝的难度。目前就只有一个方法:直接通过渲染好的网页进行信息抽取!也就只能这样了!

项目思路

  1. 根据网站分类进行分类爬取
  2. 在软件列表页面得到每个软件详情页的地址
  3. 通过请求每个软件的详情页地址来到详情页再抽取相关信息采集

开发

软件分类列表页址获取

点击windows分类:

某软件资源站采集-BUG-Fly

d可以看到url的变化: https://xxx.xxx/category/windows/
那么可以猜想:不同分类下的url构成 https://xxx.xxx/category/ +分类名/
这样分类采集的依据就有了。

再看每页的最下面:

某软件资源站采集-BUG-Fly

有页码和总页数的展示,看来还得实现翻页。点击第二页:

某软件资源站采集-BUG-Fly

可以看到第二页的url,在分类的url基础上多了:page/page_num/。
既然这样翻页逻辑就有了:
https://xxx.xxx/category/category_name/ page/page_num/
而且在每页满条目展示的情况下,自动展示9个软件条目。
下面就开始写这部分的代码。

软件分类列表页地址方法

前面提到在分类展示页面的底下有展示总页数的地方,可以抽取这部分信息截取总页数的数字:

某软件资源站采集-BUG-Fly

网页解析库选择 :Beautifulsoup4

from bs4 import BeautifulSoup as bsp
import requests

base_url = 'https://xxx.xxx/category/'
category_url = base_url + category +'/'
req = requests.get(category_url) #请求构造的分类第一页
req_txt = req.text
html = bsp(req_txt,'lxml') #实例化bsp对象
info_pages= html.find_all(attrs={'class':'pages'})[0].string #匹配标签内的总页数
total_pages = info_pages.split()[3] # 截取总页数

拿到总页数后生成每页的url:

page_offset = (offset for offset in range(1,int(total_pages)+1)) #这里考虑到性能的优化,采用列表解析生成器
for num in page_offset:
    item_url = base_url + category + '/page/' + str(num) + '/'
    yield item_url #考虑到性能优化,采用生成器函数

page_url_generator

def page_url_generator(category):
    category_url = base_url + category +'/'
    req = requests.get(category_url)
    req_txt = req.text
    html = bsp(req_txt,'lxml')
    info_pages= html.find_all(attrs={'class':'pages'})[0].string
    total_pages = info_pages.split()[3]
    page_offset = (offset for offset in range(1,int(total_pages)+1))
    for num in page_offset:
        item_url = base_url + category + '/page/' + str(num) + '/'
        yield item_url

软件详情页地址

某软件资源站采集-BUG-Fly

通过节点分析,软件缩略图带有详情页地址,软件标题也带有软件详情页地址。选哪个呢?!

通过xpath筛选发现:缩略图会筛选到两个地址,其中一个不符合标准。
而软件标题的地址是唯一的。那么就采用软件标题地址。

网页解析库选择:lxml

soft_url_generator

def soft_url_generator(pages_url):
    for url in pages_url: #遍历传进来的 page_url_generator 
        req = requests.get(url)
        req_text = req.text
        tree_html = lxml.etree.HTML(req_text) #实例化lxml.etree.HTML对象
        url_list = tree_html.xpath('//h2[@class="post-title"]/a/@href') #xpath 提取这个页面里面所有的软件详情页地址
        yield url_list

下面进行详情页解析,提取相应的信息。

某软件资源站采集-BUG-Fly

可以看到信息在相应的结点里。当我把整个详情页的解析规则写好后,我朋友却告诉我在软件下载地址那里有3种结构。看来得兼容适配其他两种,这样才会正确采集下载地址。

通过观察其他两种,发现这三种,都在不同的结点里。最后一个万能的东西该上场了,正则表达式。通过分析发现:这个下载地址在整个网页中独一无二,有着一定的特征。

在写完整个程序进行测试的时候,发现在有些软件详情页里面获取图片的时候,会报错,打开相应的网页一看,又是一种节点结构。进行兼容后又测,又报错了,没错又是一种结构,就这样兼容了5次,不知道后面还有没有,就直接写了异常捕获,进行容错。

page_parser

def page_parser(url):
    
    detail_url = url
    req = requests.get(detail_url)
    req_txt = req.text
    html = bsp(req_txt,'lxml')
    soft_name = html.find_all(attrs={"class":"post-title"})[0].string
    tree_html = lxml.etree.HTML(req_txt)
    date = tree_html.xpath('//div[@class="post-date"]/text()[2]')[0]
    soft_url = re.findall('https://uploadproper\.net\/\w{12}',req_txt,re.S)[0]
    detail_text = tree_html.xpath('//div[@class="entry"]/p//text() | //div[@class="entry"]/h2//text() | //div[@class="entry"]/h3//text() | //div[@class="entry"]/h4//text() | //div[@class="entry"]/h5//text() | //div[@class="entry"]/ul//text()')
    category = html.find_all(attrs={"rel":"category tag"})[0].string + ' / ' + html.find_all(attrs={"rel":"category tag"})[1].string
    try:
        img = tree_html.xpath('//figure/img/@src | //h2/img/@src | //p/img/@src | //div/img[@class="aligncenter wp-image-343 size-medium"]/@src | //p/a/img/@src')[0]
        info_list = [soft_name,date,category,img,soft_url,detail_text]
    except IndexError:
        img = '异常导致,请自行手动获取'
        info_list = [soft_name,date,category,img,soft_url,detail_text]

    return info_list

代码整合

import re,requests,lxml
from bs4 import BeautifulSoup as bsp
import csv


def page_url_generator(category):
    category_url = base_url + category +'/'
    req = requests.get(category_url)
    req_txt = req.text
    html = bsp(req_txt,'lxml')
    info_pages= html.find_all(attrs={'class':'pages'})[0].string
    total_pages = info_pages.split()[3]
    page_offset = (offset for offset in range(1,int(total_pages)+1))
    for num in page_offset:
        item_url = base_url + category + '/page/' + str(num) + '/'
        yield item_url


def soft_url_generator(pages_url):
    for url in pages_url: #遍历传进来的 page_url_generator 
        req = requests.get(url)
        req_text = req.text
        tree_html = lxml.etree.HTML(req_text) #实例化lxml.etree.HTML对象
        url_list = tree_html.xpath('//h2[@class="post-title"]/a/@href') #xpath 提取这个页面里面所有的软件详情页地址
        yield url_list


def page_parser(url):
    
    detail_url = url
    req = requests.get(detail_url)
    req_txt = req.text
    html = bsp(req_txt,'lxml')
    soft_name = html.find_all(attrs={"class":"post-title"})[0].string
    tree_html = lxml.etree.HTML(req_txt)
    date = tree_html.xpath('//div[@class="post-date"]/text()[2]')[0]
    soft_url = re.findall('https://uploadproper\.net\/\w{12}',req_txt,re.S)[0]
    detail_text = tree_html.xpath('//div[@class="entry"]/p//text() | //div[@class="entry"]/h2//text() | //div[@class="entry"]/h3//text() | //div[@class="entry"]/h4//text() | //div[@class="entry"]/h5//text() | //div[@class="entry"]/ul//text()')
    category = html.find_all(attrs={"rel":"category tag"})[0].string + ' / ' + html.find_all(attrs={"rel":"category tag"})[1].string
    try:
        img = tree_html.xpath('//figure/img/@src | //h2/img/@src | //p/img/@src | //div/img[@class="aligncenter wp-image-343 size-medium"]/@src | //p/a/img/@src')[0]
        info_list = [soft_name,date,category,img,soft_url,detail_text]
    except IndexError:
        img = '异常导致,请自行手动获取'
        info_list = [soft_name,date,category,img,soft_url,detail_text]

    return info_list

if __name__ == "__main__":
    i = 1
    category = input('请输入分类:')
    pages = page_url_generator(category)
    for url_list in soft_url_generator(pages):
        for url in url_list:
            with open('info.csv','a',encoding='utf-8') as f:
                writer = csv.writer(f)
                sim = page_paser(url)
                print(sim)
                writer.writerow(sim)
                if url_list.index(url)==len(url_list)-1:

                    print('第'+ str(i) +'页爬取完毕')
        i = i+1

项目总结

  • 采用面向过程开发
  • 应用生成器,进行了性能优化
  • 待优化:采用多线程,可大大缩短采集时间
  • 待优化:实现增量更新爬取
Python实战项目
12
0
BUG-Fly
写BUG飞起的Coder
JetBrains全系列软件激活
上一篇
Python基础语法
下一篇

评论 (0)

请登录以参与评论
现在登录
    发表评论

猜你喜欢

  • 如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求
  • Python 函数
  • Python内置数据结构——列表(list)
  • Python基础语法

词云

2020 (1) Flask (1) Go (1) JS逆向 (1) Linux (1) Playwright (1) PySide2开发 (1) Python (13) Python实战项目 (5) 固原一中 (1) 国庆70周年 (1) 开源 (1) 数据结构和算法 (2) 数组 (1) 新年贺词 (1) 新月诗刊社 (3) 电脑评测 (3) 软件教程 (3) 雨雯公益 (1) 音乐 (3)

BUG-Fly

写BUG飞起的Coder
34
文章
5
评论
324
获赞
  • 首页
  • 友情链接
Copyright © 2019-08-20-2025 BUG-Fly. Designed by BUG-Fly.

Fly小站已经运行:

津ICP备19007312号
技术基佬基地: KRUNK ZHOU Legna 科技
  • Python13
  • Python实战项目5
  • 新月诗刊社3
  • 音乐3
  • 电脑评测3
  • 首页
  • BUG-EXP
  • 编程开发
  • 电脑评测
  • 生活分享
  • 友情链接
  • Fly全站协议声明