网络爬虫是个比较有意思的东西,就像自然界的蚂蚁大军一样,在浩瀚的网络世界搜寻目标,从而得到我们想要的数据。用 Python 做爬虫非常容易,我用爬取豆瓣音乐的例子进行说明。

爬虫一般分为三个步骤:

  1. 抓取网页
  2. 解析网页
  3. 存储数据

下面我们就分三个步骤进行操作。

1. 抓取网页

我使用 requests 库进行网络请求,代码非常简洁,拿到原始的 html 文本就可以。

import requests
import logging

# 定义日志输出格式 eg: 2017-12-10 21:50:14,021 HelloWorld.py (21) __main__ [DEBUG]:debug message
FORMAT = '%(asctime)s %(filename)s (%(lineno)d) %(name)s [%(levelname)s]:%(message)s'
# 打开日志,允许输出 debug 及以上级别的日志
logging.basicConfig(level=logging.DEBUG, format=FORMAT)
# 关闭日志,禁止输出 critical 及以下级别日志
# logging.disable(level=logging.CRITICAL)
# 建议使用模块名称作为日志名称
log = logging.getLogger(__name__)

def __request_html(url):
    # 加上 UA 请求头模拟浏览器请求,使用代理防止 IP 被封
    proxies = {'http': 'http://27.40.158.52:61234'}
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.4295.400 QQBrowser/9.7.12661.400'}
    response = requests.get(url, headers=headers, proxies=proxies)
    if response.status_code == 200:
        log.info('status ok')
        # 响应的 text 就是 html 文本
        return response.text
    else:
        log.debug('status:'.format(response.status_code))
        return None

2. 解析网页

使用 lxml 解析网页,xpath 定位元素。在 Chrome 浏览器中打开开发者模式,在 Elements 模块找到要抓去的数据,然后右键拷贝元素的 xpath,放到代码里使用就可以了。

xpath 基础语法:

  • // 双斜杠 定位根节点,会对全文进行扫描,在文档中选取所有符合条件的内容,以列表的形式返回。
  • / 单斜杠 寻找当前标签路径的下一层路径标签或者对当前路标签内容进行操作
  • /text() 获取当前路径下的文本内容
  • /@xxxx 提取当前路径下标签的属性值
  • | 可选符 使用|可选取若干个路径 如//p | //div 即在当前路径下选取所有符合条件的p标签和div标签。
  • . 点 用来选取当前节点
  • .. 双点 选取当前节点的父节点
  • xpath 返回值是 list 类型
from lxml import etree

def __parse_html(text):
    if text is None or len(text) <= 0:
        return
    
    es = etree.HTML(text)
    # xpath 返回的数据类型是 list
    # 当前路径下的文本,使用 /text()
    # 当前路径下的链接,使用 /@href
    # 当前路径下的图片,使用 /@src
    title_x = es.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')
    title = title_x[0].strip()
    rate_x = es.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[2]/text()')
    rate = rate_x[0].strip()
    numbers_x = es.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[3]/text()')
    numbers = numbers_x[0].strip()
    url_x = es.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/@href')
    url = url_x[0].strip()
    image_x = es.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[1]/a/img/@src')
    image = image_x[0].strip()
    desc_x = es.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/p/text()')
    desc = desc_x[0].strip()
    numbers = __format_text(numbers)
    music = {
        'title': title,
        'rate': rate,
        'url': url,
        'numbers': numbers,
        'image': image,
        'description': desc
    }
    log.debug('music:{}'.format(music))
    return music

# 去除换行和空格
def __format_text(text):
    a = text[1: len(text) - 1].strip()
    return a

3. 存储数据

通过解析网页拿到数据后,就可以保存了,比如保存到数据库,excel 文件。这里,我使用 MySQL 数据库作为示例。

创建对应的数据表后,我们引入 pymysql 库,进行数据库操作。执行插入就可以了,注意事务的提交和恢复,以及连接的关闭。

import pymysql

__USER = 'root'
__PASSWORD = 'xxxxxx'
__LOCAL_HOST = '127.0.0.1'
__PORT = '3306'
__DATABASE = 'python_data'
__TABLE = 'db_music'

def __save2db(music):
    sql = 'insert into ' + __TABLE + ' (title, rate, url, numbers, image, description) values (%s, %s, %s, %s, %s, %s)'
    conn = pymysql.connect(host=__LOCAL_HOST, user=__USER, passwd=__PASSWORD, db=__DATABASE, charset='utf8')
    cur = conn.cursor()
    affected_rows = 0
    try:
        affected_rows = cur.execute(sql, (music['title'], music['rate'], music['url'], music['numbers'], music['image'], music['description']))
        conn.commit()
    except BaseException as e:
        log.exception(e)
        conn.rollback()
    finally:
        log.info('affected_rows:{}'.format(affected_rows))
        cur.close()
        conn.close()

最后,在使用的时候,就可以这么调用。

__MUSIC_URL = 'https://music.douban.com/top250/'

if __name__ == '__main__':
    log.debug('run main')
    __html_text = __request_html(__MUSIC_URL)
    __music = __parse_html(__html_text)
    __save2db(__music)

来看一下我们抓到的数据,就是豆瓣音乐 top 250。

这只是个小应用,如果有更高的要求,可以使用 Scrapy 框架,它更强大。