学习urllib.request和beautifulsoup,并从dribbble和behance上爬取了一些图片,记录一下。

一、urllib.request

1. url的构造

构造请求的url遇到的主要问题是如何翻页的问题,dribbble网站是下拉到底自动加载下一页,地址栏的url没有变化,如下:

Python爬虫(urllib.request和BeautifulSoup)-LMLPHP

但是通过检查,我们可以发现request url里关于page的字段,如下:

Python爬虫(urllib.request和BeautifulSoup)-LMLPHP

因此,我们构造如下的url:

for i in range(25):  # 最多25页
    url = 'https://dribbble.com/shots?page=' + str(i + 1) + '&per_page=24'

2. header的构造

不同网页需要的header的内容不一样,参照检查里request header来构造。例如dribbble需要Referer,即从哪一个页面跳转到这个当前页面的,一般填写网站相关页面网址就可以。

headers = {"Accept": "text/html,application/xhtml+xml,application/xml;",
           "Referer": "https://dribbble.com/",
           "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36"}

3. urllib.request获取页面内容

用url和header实例化一个urllib.request.Request(url, headers),然后url.request.urlopen()访问网页获取数据,使用read()函数即可读取页面内容。

def open_url(url):
    # 将Request类实例化并传入url为初始值,然后赋值给req
    headers = {"Accept": "text/html,application/xhtml+xml,application/xml;",
               "Referer": "https://dribbble.com/",
               "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36"}
    req = urllib.request.Request(url, headers=headers)
    # 访问url,并将页面的二进制数据赋值给page
    res = urllib.request.urlopen(req)
    # 将page中的内容转换为utf-8编码
    html = res.read().decode('utf-8')
    return html

这里需要注意的是,有的页面返回的数据是“text/html; charset=utf-8”格式,直接decode('utf-8')编码即可,而有的页面返回的是“application/json; charset=utf-8”格式数据,例如behance:

Python爬虫(urllib.request和BeautifulSoup)-LMLPHP

此时就需要json.loads()来获取数据,得到的是列表,用操作列表的方式拿到html数据:

 html = json.loads(res.read())
 return html['html']

二、BeautifulSoup

BeautifulSoup将复杂的html文档转换为树形结构,每一个节点都是一个对象。

1.创建对象

soup = BeautifulSoup(open_url(url), 'html.parser')

‘html.parser’是解析器,BeautifulSoup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐安装,常见解析器:

Python爬虫(urllib.request和BeautifulSoup)-LMLPHP

2. 标签选择器

标签选择筛选功能弱但是速度快,通过这种“soup.标签名” 我们就可以获得这个标签的内容,但通过这种方式获取标签,如果文档中有多个这样的标签,返回的结果是第一个标签的内容

# 获取p标签
soup.p

# 获取p标签的属性的两种方法
soup.p.attrs['name']
soup.p['name']

# 获取第一个p标签的内容
soup.p.string

# 获取p标签下所有子标签,返回一个列表
soup.p.contents

# 获取p标签下所有子标签,返回一个迭代器
for i,child in enumerate(soup.p.children):
    print(i,child)

# 获取父节点的信息
soup.a.parent

# 获取祖先节点
list(enumerate(soup.a.parents))

# 获取后面的兄弟节点
soup.a.next_siblings

# 获取前面的兄弟节点
soup.a.previous_siblings

# 获取下一个兄弟标签
soup.a.next_sibling

# 获取上一个兄弟标签
souo.a.previous_sinbling

3. 标准选择器

find_all(name,attrs,recursive,text,**kwargs)可以根据标签名,属性,内容查找文档,返回一个迭代器,例如:

# 获取所有class为js-project-module--picture的所有img标签,并选择每个标签的src构成一个列表
image.src = [item['src'] for item in soup.find_all('img', {"class": "js-project-module--picture"})]

# .string获取div的内容,strip()去除前后空格
desc = soup.find_all('div', {"class": "js-basic-info-description"})
if desc:
    image.desc = [item.string.strip() for item in desc]

find(name,attrs,recursive,text,**kwargs),返回匹配的第一个元素

其他一些类似的用法:
find_parents()返回所有祖先节点,find_parent()返回直接父节点
find_next_siblings()返回后面所有兄弟节点,find_next_sibling()返回后面第一个兄弟节点
find_previous_siblings()返回前面所有兄弟节点,find_previous_sibling()返回前面第一个兄弟节点
find_all_next()返回节点后所有符合条件的节点, find_next()返回第一个符合条件的节点
find_all_previous()返回节点后所有符合条件的节点, find_previous()返回第一个符合条件的节点

三、完整代码

# -*- coding: utf-8 -*-

"""
批量获取图片页面链接
保存到 dribbble_list.txt 文件中
根地址: https://dribbble.com/
"""

import random
import urllib.request
from bs4 import BeautifulSoup
import os
import time


def open_url(url):
    # 将Request类实例化并传入url为初始值,然后赋值给req
    headers = {"Accept": "text/html,application/xhtml+xml,application/xml;",
               "Referer": "https://dribbble.com/",
               "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3493.3 Safari/537.36"}
    req = urllib.request.Request(url, headers=headers)
    # 访问url,并将页面的二进制数据赋值给page
    res = urllib.request.urlopen(req)
    # 将page中的内容转换为utf-8编码
    html = res.read().decode('utf-8')
    return html


# 打开/创建“dribbble_list.txt”文件,O_CREAT:不存在即创建、O_WRONLY:只写、O_APPEND:追加
fd = os.open('dribbble_list.txt', os.O_CREAT | os.O_WRONLY | os.O_APPEND)
for i in range(25):  # 最多25页
    url = 'https://dribbble.com/shots?page=' + str(i + 1) + '&per_page=24'
    soup = BeautifulSoup(open_url(url), 'html.parser')
    srcs = soup.find_all('a', {"class": "dribbble-link"})
    src_list = [src['href'] for src in srcs]
    for src in src_list:
        os.write(fd, bytes(src, 'UTF-8'))
        os.write(fd, bytes('\n', 'UTF-8'))
    time.sleep(random.random()*5)

 

10-06 17:10