Python《使用lxml解析xpath--爬取konachan》

今天尝试使用 lxml 来解析 xpath的文档,其实也就是html文档了啦。

一:lxml和xpath

lxml库是一个XML、HTML的解析器,主要用于解析和提取XML、HTML数据。lxml库先将HTML文档解析,然后就可以使用XPath 搜索或遍历HTML文档中的节点。
首先得预先安装lxml,conda install lxml

XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历,而将 HTML文档转换成 XML文档后,就可以用 XPath 查找 HTML 节点或元素。

具体细节的用法和介绍呢,这些都是常识类的知识了,请参考https://www.jianshu.com/p/a7633dd72a3f 或者自行百度。

下面是一个简要的学习测试的例子:

from lxml import etree

# HTML字符串
text = '''
<bookstore>
  <book>
    <title lang="en">Harry Potter</title>
    <author>J K. Rowling</author>
    <name>杰克罗琳</name>
    <kep>aaa</kep>
    <kep>bbb</kep>
    <year>2005</year>
    <price>29.99</price>
  </book>
</bookstore>
'''

# 使用HTML()方法解析字符串
# HTML()默认使用的就是HTML解析器,如果遇到不规范的HTML代码,会自动补全。
html_element = etree.HTML(text)

# 解析后就可以调用xpath方法了,默认返回列表,空也是列表。
name = html_element.xpath('//book/name/text()') # 返回值:['杰克罗琳']
print(name)
name = html_element.xpath('//book/kep/text()') # 返回值:['aaa', 'bbb']
print(name)
name = html_element.xpath('//book/no/text()') # 返回值:[]
print(name)

二:爬取网站https://konachan.com/post

https://konachan.com/post 有很多很多动漫图片,包括很多YY的。
这不是重点,重点是我们今天需要做个小爬虫给它爬下来,尝试使用lxml的功能。

废话不多说,先来看看网站的首页,鉴于设计内容可能不良,因此我拿计算机遮盖了啊,自行查看。
在这里插入图片描述

我们再来看下url有啥特征呢。
https://konachan.com/post?page=3&tags=azur_lane
是由两部分组成,一个是页号,一个是标签,因此我们首先需要找到标签有哪些。

我们先去标签页去把所有的标签撸下来,这里是个分页,我们需要每个都访问下。
在这里插入图片描述

每个标签页,我们只保存有数据的标签次啊记录下来
在这里插入图片描述

有了标签,就进入该标签的页面
https://konachan.com/post? tags=azur_lane 是等于https://konachan.com/post?tags=azur_lane &page=1
因此只需要拼装页面即可访问到某个标签的某个分页面

到了具体的页面后呢。我们发现有超清大图的展示,因此我们直接抓取超清大图的链接。
在这里插入图片描述

按照习惯,我们先下载一个看看:
在这里插入图片描述

测试代码如下:

import requests

def run8():
    headers = {
    
    
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
        "Referer": "https://konachan.com/"
    }
    with open("D:/estimages/mn.jpg", "wb") as f :
        f.write(requests.get("https://konachan.com/image/7c7f3625319166ecfb9a79895d020afd/Konachan.com%20-%20300620%20applekun%20blonde_hair%20fate_grand_order%20fate_%28series%29%20long_hair%20navel%20necklace%20ponytail%20ribbons%20saber%20saber_alter%20shorts%20sword%20weapon%20white%20yellow_eyes.jpg", headers=headers).content)
        f.close

if name == “main”: #主程序入口
run8() #调用上面的run方法

测试成功!
在这里插入图片描述

完整代码如下:
建立两个线程,一个线程专门去抓取标签,另一个专门去根据标签下载图片

import os
import requests
from bs4 import BeautifulSoup
from lxml import etree
import time
import threading

rootrurl = 'https://konachan.com/'
save_dir = 'D:/estimages/'
no_more_pages = 'END'
max_pages = 10

# 这是一个tag集合,tag不能重复
tag_cache = set()


headers = {
    
    
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
    "Referer": "https://konachan.com/"
}

def getTotalPages(url):

    # 倒数第二个 <a>下面的<font><font>的text就是
    total_page_nums = 1
    html = BeautifulSoup(requests.get(url, headers=headers).text, features="html.parser")
    div = html.find('div', attrs={
    
    'class': 'pagination'})

    if div is not None:
        # 使用HTML()方法解析字符串
        # HTML()默认使用的就是HTML解析器,如果遇到不规范的HTML代码,会自动补全。
        html_element = etree.HTML(str(div))
        total_page_nums = int(html_element.xpath('//div/a/text()')[-2])

    return total_page_nums


def getOneTagPage(url):
    html = BeautifulSoup(requests.get(url, headers=headers).text, features="html.parser")
    trs = html.find('table', attrs={
    
    'class': 'highlightable'}).find('tbody').find_all('tr')

    for tr in trs:
        html_element = etree.HTML(str(tr))
        num = int(html_element.xpath('//tr/td/text()')[0])
        if num == 0:
            continue

        tag = html_element.xpath('//tr/td/a/@href')[1]
        if tag not in tag_cache:
            tag_cache.add(tag)


def getAllTags():
    url = rootrurl + 'tag?order=date'

    # Step 1: 获得总共多少页
    total_page_nums = getTotalPages(url)

    # Step 2: 自己构造页面爬取所有的tag
    for i in range(1, (total_page_nums + 1)):
    # for i in range(1550, (total_page_nums + 1)):   # 为了测试方便, 直接从 1550页开始了。用户可以自行改回来,从第1页开始。
        print("tag page num : %d." % i)
        url = rootrurl + ('tag?order=date&page=%d' % i)
        getOneTagPage(url)


def saveOnePage(tag, idx, saveDir):
    url = rootrurl + tag[1:] + '&page=%d' % idx
    html = BeautifulSoup(requests.get(url, headers=headers).text, features="html.parser")
    a_s = html.find('ul', attrs={
    
    'id': 'post-list-posts'})
    if a_s is None:
        return
    a_s = a_s.find_all('a', attrs={
    
    'class': 'directlink largeimg'})

    i = 1
    for a in a_s:
        img_url = a.get('href')
        img = requests.get(img_url)  # 请求图片的实际URL
        with open(
                '{}/{}_{}.{}'.format(saveDir, idx, i, img_url.split(".")[-1]), 'wb') as jpg:  # 请求图片并写进去到本地文件
            jpg.write(img.content)
            i = i+1


def saveOneTag(tag):
    dir = '{}{}'.format(save_dir, tag.split("=")[-1])
    if not os.path.exists(dir):
        os.mkdir(dir)

    print(tag)
    url = rootrurl + tag[1:]
    tag_cache.remove(tag)  # 避免重复操作该 tag

    # Step 1: 获得总共多少页
    total_page_nums = getTotalPages(url)

    # Step 2: 便利每个页面进行下载
    for i in range(1, (total_page_nums + 1)):
        if i > 6:  # 此处纯属是为了方便测试,不然有的页面太多了啊
            break
        saveOnePage(tag, i, dir)





def saveAllTagImgs():

    # 临时得到一批 tag
    l = list(tag_cache)

    # 对每个tag进行访问操作
    for tag in l:
        saveOneTag(tag)



class myThread1 (threading.Thread):   #继承父类threading.Thread
    def __init__(self, threadID):
        threading.Thread.__init__(self)
        self.threadID = threadID

    def run(self):                   #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
        print("Thread %d is running..." % self.threadID)
        getAllTags()
        print("Thread %d is over..." % self.threadID)

class myThread2 (threading.Thread):   #继承父类threading.Thread
    def __init__(self, threadID):
        threading.Thread.__init__(self)
        self.threadID = threadID

    def run(self):                   #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
        print("Thread %d is running..." % self.threadID)
        while 1:
            time.sleep(5)  # wait to get all tags
            if len(tag_cache) == 0:
                break
            saveAllTagImgs()
        print("Thread %d is over..." % self.threadID)

if __name__ == '__main__':
    # get all tags
    # getAllTags()

    # get all imgs according to tags
    # saveAllTagImgs()
    # print(len(tag_cache))


    thread1 = myThread1(1)  # 创建多个线程去分别爬取各个标签页的数据
    thread1.start()

    thread2 = myThread2(2)  # 创建多个线程去分别爬取各个标签页的数据
    thread2.start()

    while 1:
        time.sleep(1)

效果如下:
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_29367075/article/details/111088096