Python自定义爬虫示例

本文介绍了一种基于Python的HTML解析器实现的京东商品信息爬虫,详细讲解了如何利用栈来遍历HTML标签,精确抓取商品价格、名称等关键信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、示例说明

python版本:Python3.7

操作系统:win10 64位

爬虫类:HTMLParser

思路:利用栈,把标签入栈出栈的方式遍历标签

 

2、修改主要方法

2.1、标签开始时调用的方法 handle_starttag

标签开始处,把标签入栈,标签属性入栈

    def handle_starttag(self, tag, attrs):
        HTMLParser.handle_starttag(self, tag, attrs)
        # SGMLParser.finish_starttag(self, tag, attrs)
        # self.now_tag_attrs = {} if attrs is None else attrs

        #没有属性的标签,设置空列表站位
        if attrs == None:
            attrs = {}

        self.now_tag_attrs.append(attrs)
        self.tag_stack.append(tag)

2.2、标签结束时调用的方法 handle_endtag

标签结束处,找寻栈中的标签;

如果是该标签的开始标签,则出栈,并结束循环,

如果不是该标签的开始标签,则出栈,并继续判断

    def handle_endtag(self, tag):
        HTMLParser.handle_endtag(self, tag)
        # SGMLParser.finish_endtag(self, tag)
        # self.now_tag_attrs = {}
        # 一直pop,直到踢出与当前endtag匹配的为止。
        # 这种现象是由于出现<meta/>这种只有starttag的标签导致的
        while True:
            if operator.eq(tag, self.tag_stack[-1]):
                self.tag_stack.pop()
                self.now_tag_attrs.pop()
                break
            else:
                self.tag_stack.pop()
                self.now_tag_attrs.pop()

2.3、获取标签值的方法 handle_data

判断栈中的层级信息,是否与给出的标签栈相同,相同则说明是自己要取的标签,记录值

说明:标签栈可以理解为标签的层级关系,如下:

('html', 'body', 'div', 'div', 'div', 'div', 'div', 'div', 'ul', 'li', 'div', 'div', 'strong', 'em')
    def handle_data(self, text):
        BaseParser.handle_data(self, text)

        if text == "":
            return

        # id
        if operator.eq(tuple(self.tag_stack), MyJDParser.__priceStyle_stack__) \
                and self.is_now_has_tag_key(MyJDParser.__priceStyle_tag_key_set__):
            self.priceStyle = text

 

2.4、自定义方法,判断标签属性是否合法

在方法handle_data中与标签栈配合使用,检查该标签是发包含给定的属性,例如属性:

('target', 'title','href','onclick')
    def is_now_has_tag_key(self, key_set):
        if len(self.now_tag_attrs) == 0 and len(key_set) == 0:
            return True

        # 获取该标签的属性
        attrs = {}
        attrs = self.now_tag_attrs[-1]

        if len(attrs) == 0 and len(key_set) == 0:
            return True
        elif len(attrs) == 0 and len(key_set) > 0:
            return False

        for  key in key_set:
            kv_index = 0
            for kv in attrs:
                kv_index += 1
                if operator.eq(key, kv[0]):
                    break
                if len(self.now_tag_attrs) == kv_index:
                    return False


        return True

 

2.5、自定义方法,获取标签属性值

方法可在handle_data中调用,如果是自己要的标签,则可以获取改标签对应属性值,例如获取href链接

    def __getAttrValue(self, key=''):
        if len(self.now_tag_attrs) == 0:
            return ''

        # 获取该标签的属性
        attrs = {}
        attrs = self.now_tag_attrs[-1]
        for attr in attrs:
            if operator.eq(str(attr[0]), key):
                return str(attr[1])

        return ''

3、爬虫软件调用方法

parser = MyJDParser()
parser.feed(html)
goods = parser.bean_list;

4、小技巧

4.1、获取对应标签的标签栈

抓取的html页面中,利用关键字搜索到标签所在的位置,关键字为标签的值,

在方法handle_data中加入如下内容,debug即可获取准确的标签栈

  def handle_data(self, text):
        BaseParser.handle_data(self, text)

        if text == "":
            return
        
        if operator.eq(text, '标签值'):
            print(tuple(self.tag_stack))

        # id
        if operator.eq(tuple(self.tag_stack), MyJDParser.__priceStyle_stack__) \
                and self.is_now_has_tag_key(MyJDParser.__priceStyle_tag_key_set__):
            self.priceStyle = text

5、完整代码如下

示例为抓取京东商城页面的商品信息

#! /usr/bin/python
# coding:utf-8

# from sgmllib import SGMLParser
from html.parser import HTMLParser
import operator


class BaseParser(HTMLParser):
    def __init__(self):
        # SGMLParser.__init__(self)
        HTMLParser.__init__(self)

        # 保存抓取的标签
        self.tag_stack = []

        # 保存抓取的标签属性(于tag_stack对应)
        self.now_tag_attrs = []


    def handle_starttag(self, tag, attrs):
        HTMLParser.handle_starttag(self, tag, attrs)
        # SGMLParser.finish_starttag(self, tag, attrs)
        # self.now_tag_attrs = {} if attrs is None else attrs

        #没有属性的标签,设置空列表站位
        if attrs == None:
            attrs = {}

        self.now_tag_attrs.append(attrs)
        self.tag_stack.append(tag)
        # print("start_tag: " + str(self.tag_stack))
        # print("start_attrs: " + str(attrs))

    def handle_endtag(self, tag):
        HTMLParser.handle_endtag(self, tag)
        # SGMLParser.finish_endtag(self, tag)
        # self.now_tag_attrs = {}
        # 一直pop,直到踢出与当前endtag匹配的为止。
        # 这种现象是由于出现<meta/>这种只有starttag的标签导致的
        while True:
            if operator.eq(tag, self.tag_stack[-1]):
                self.tag_stack.pop()
                self.now_tag_attrs.pop()
                break
            else:
                self.tag_stack.pop()
                self.now_tag_attrs.pop()
        # print("  end_tag: " + str(self.tag_stack))

    def is_now_has_tag_key(self, key_set):
        if len(self.now_tag_attrs) == 0 and len(key_set) == 0:
            return True

        # 获取该标签的属性
        attrs = {}
        attrs = self.now_tag_attrs[-1]

        if len(attrs) == 0 and len(key_set) == 0:
            return True
        elif len(attrs) == 0 and len(key_set) > 0:
            return False

        for  key in key_set:
            kv_index = 0
            for kv in attrs:
                kv_index += 1
                if operator.eq(key, kv[0]):
                    break
                if len(self.now_tag_attrs) == kv_index:
                    return False


        return True

    def get_data(self):
        pass


class MyJDParser(BaseParser):
    # priceStyle
    __priceStyle_stack__ = ('html', 'body', 'div', 'div', 'div', 'div', 'div', 'div', 'ul', 'li', 'div', 'div', 'strong', 'em')
    __priceStyle_tag_key_set__ = ()

    # price
    __price_stack__ = ('html', 'body', 'div', 'div', 'div', 'div', 'div', 'div', 'ul', 'li', 'div', 'div', 'strong', 'i')
    __price_tag_key_set__ = ()

    # 名称
    __href_stack__ = ('html', 'body', 'div', 'div', 'div', 'div', 'div', 'div', 'ul', 'li', 'div', 'div', 'a')
    __href_tag_key_set__ = ('target', 'title','href','onclick')

    #名称
    __name_stack__ = ('html', 'body', 'div', 'div', 'div', 'div', 'div', 'div', 'ul', 'li', 'div', 'div', 'a', 'em')
    __name_tag_key_set__ = ()

    # 名称
    __name_font_stack__ = ('html', 'body', 'div', 'div', 'div', 'div', 'div', 'div', 'ul', 'li', 'div', 'div', 'a', 'em','font')
    __name_font_tag_key_set__ = ()

    # 类型
    __promo_words_stack__ = ('html', 'body', 'div', 'div', 'div', 'div', 'div', 'div', 'ul', 'li', 'div', 'div', 'a', 'i')
    __promo_words_tag_key_set__ = ('class','id')


    def __init__(self):
        BaseParser.__init__(self)
        self.content_set = {}
        self.priceStyle = ''
        self.price = '0'
        self.name = ''
        self.promo_words = ''
        self.href = ''
        self.bean_list = []

    def handle_data(self, text):
        BaseParser.handle_data(self, text)

        if text == "":
            return

        if operator.eq(text, '标签值'):
            print(tuple(self.tag_stack))

        # id
        if operator.eq(tuple(self.tag_stack), MyJDParser.__priceStyle_stack__) \
                and self.is_now_has_tag_key(MyJDParser.__priceStyle_tag_key_set__):
            self.priceStyle = text
        # level
        elif operator.eq(tuple(self.tag_stack), MyJDParser.__price_stack__) \
                and self.is_now_has_tag_key(MyJDParser.__price_tag_key_set__):
            self.price = text
        # # title
        elif operator.eq(tuple(self.tag_stack), MyJDParser.__href_stack__) \
            and self.is_now_has_tag_key(MyJDParser.__href_tag_key_set__):
            self.href = 'https:' + self.__getAttrValue(key='href')

        elif operator.eq(tuple(self.tag_stack), MyJDParser.__name_stack__) \
                and self.is_now_has_tag_key(MyJDParser.__name_tag_key_set__):
            self.name += text
        # # error_type
        elif operator.eq(tuple(self.tag_stack), MyJDParser.__name_font_stack__) \
             and self.is_now_has_tag_key(MyJDParser.__name_font_tag_key_set__):
            self.name += text
        elif operator.eq(tuple(self.tag_stack), MyJDParser.__promo_words_stack__) \
                and self.is_now_has_tag_key(MyJDParser.__promo_words_tag_key_set__) :
            self.promo_words = text
            good = {}
            good['priceStyle'] = self.priceStyle
            good['price'] = self.price
            good['name'] = self.name
            good['promo_words'] = self.promo_words
            good['href'] = self.href

            self.bean_list.append(good)
            self.priceStyle = ''
            self.price = '0'
            self.name = ''
            self.promo_words = ''
            self.href = ''

    def __getAttrValue(self, key=''):
        if len(self.now_tag_attrs) == 0:
            return ''

        # 获取该标签的属性
        attrs = {}
        attrs = self.now_tag_attrs[-1]
        for attr in attrs:
            if operator.eq(str(attr[0]), key):
                return str(attr[1])

        return ''

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值