Python网络爬虫案例实战:静态网页爬取:Requests爬虫实践

Python网络爬虫案例实战:静态网页爬取:Requests爬虫实践

至此,已经介绍了利用爬虫网络对静态网页进行爬取,下面直接通过两个实例来演示爬虫的实践。

3.12.1状态码521网页的爬取

1.问题发现
在做代理池的时候,发现了一种以前没有见过的反爬虫机制。在用常规的requests.get(url)方法对目标网页进行爬取时,其返回的状态码(status_code)为521,这是一种以前没有见过的状态码。再输出它的爬取内容(text),发现是一些JavaScript 代码,如图3-2所示。下面来探索一下。
在这里插入图片描述
2.分析原理
打开 Fiddler,爬取访问网站的包,如图3-3所示,发现浏览器对于同一网页连续访问了两次,第一次的访问状态码为521,第二次为200(正常访问)。看来网页加了反爬虫机制,需要两次访问才可返回正常网页。
在这里插入图片描述
下面来对比两次请求的区别。521的请求如图3-4 所示;200的请求如图3-5所示。
在这里插入图片描述
在这里插入图片描述
通过对比两次请求头,可发现第二次访问带了新的 Cookie值。再考虑上面程序对爬取结果的输出为JavaScript代码,可以考虑其操作过程为:第一次访问时服务器返回一段可动态生成 Cookie值的JavaScript代码;浏览器运行JavaScript 代码生成 Cookie值,并带Cookie重新进行访问;服务器被正常访问,返回页面信息,浏览器渲染加载。
3.执行流程
弄清楚浏览器的执行过程后,就可以模拟其行为通过Python 进行网页爬取。操作步骤如下:

  • 用request.get(url)获取JavaScript 代码。
  • 通过正则表达式对代码进行解析,获得JavaScript函数名,JavaScript函数参数和JavaScript函数主体,并将执行函数
    eval()语句修改为return语句返回 Cookie值。
  • 调用 execjs库的 executeJS()功能执行JavaScript代码获得 Cookie值。
  • 将Cookie值转化为字典格式,用request.get(url,Cookie = Cookie)方法获取得到正确的网页信息。
    4.实现代码
    根据以上流程步骤,实现代码主要表现在:
    (1)实现程序所需要用到的库。
import re         #实现正则表达式
import execjs     #执行JavaScript 代码
import requests   #爬取网页

(2)第一次爬取获得包含JavaScript函数的页面信息后,通过正则表达式对代码进行解析,获得JavaScript函数名、JavaScript函数参数和JavaScript函数主体,并将执行函数eval()语句修改为return语句返回 Cookie值。

#js_html为获得的包含JavaScript函数的页面信息
#提取 JavaScript函数名
js_func_name = ''.join(re.findall(r'setTimeout\(\"(\D+ )\(\d+\)\"', js_htm1))
#提取 JavaScript函数参数
js_func_param = ''.join(re.findall(r'setTimeout\(\"(\D+ )\(\d+\)\"', js_htm1))
#提取 JavaScript函数主体
js_func = ''.join(re.findall(r'(function . *?)</script>',js_htm1))

(3)将执行函数 eval()语句修改为return语句返回 Cookie值。

#修改 JavaScript函数,返回 Cookie值
js_func = js_func.replace('eval("qo = eval;qo(po);")',' return po')

(4)调用 execjs库的executeJS()功能执行JavaScript代码获得 Cookie值。

#执行 JavaScript 代码的函数,参数为 JavaScript函数主体,JavaScript函数名和 JavaScript函数参数
def executeJS(js_func, js_func_name, js_func_param):
    jscontext = execjs.compile(js_func)    #调用 execjs.compile()加载JavaScript函数主体内容
    func = jscontext.call(js_func_name, js_func_param)    # 使用call()通过函数名和参数执行该函数
    return func
cookie_str = executeJS(js_func, js_func_name,js_func_param)

(5)将Cookie值转化为字典格式。

#将Cookie值解析为字典格式,方便后面调用
def parseCookie(string):
    string = string.replace("document.cookie = '", "")
    clearance = string.split(';')[0]
    return{clearance.split('=')[0]:clearance.split('=')[1]}
cookie = parseCookie(cookie_str)

至此,在获得 Cookie后,采用带 Cookie的方式重新进行爬取,即可获得我们需要的网页信息。

3.12.2TOP250电影数据

本实践项目的目的是获取豆瓣电影TOP250的所有电影名称,网页地址为:https:
//movie.douban.com/top250。在此爬虫中,将请求头定制为实际浏览器的请求头。
1.网站分析
打开豆瓣电影TOP250的网站,右击网页的任意位置,在弹出的快捷菜单中单击“审查元素”命令即可打开该网页的请求头,如图3-6所示。
在这里插入图片描述
提取网站中重要的请求头代码为:

import requests
 headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
        'Host': 'movie.douban.com'
    } 

第一页只有25部电影,如果要获取所有的250页电影,就需要获取总共10页的内容。通过单击第二页可以发现网页地址变成了:

https://movie.douban.com/top250?start = 25

第三页的地址为https://movie.douban.com/top250?start=50,这很容易理解,每多一页,就给网页地址的start参数加上25。
2.项目实践
通过以上分析,可以使用requests获取电影网页的代码,并利用for循环翻页。代码为:

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

import requests

def get_movies():
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
        'Host': 'movie.douban.com'
    }
    
    for i in range(10):  # 豆瓣电影 Top 250 分为 10 页
        start = i * 25
        link = f'https://movie.douban.com/top250?start={start}'
        try:
            r = requests.get(link, headers=headers, timeout=10)
            r.raise_for_status()  # 检查请求是否成功
            print(f"第 {i+1} 页响应状态码:{r.status_code}")
            print(r.text)  # 打印页面内容
        except requests.RequestException as e:
            print(f"请求第 {i+1} 页失败:{e}")

get_movies()  # 调用函数

运行程序,输出如下:

9 页响应状态码:200
<!DOCTYPE html>
<html lang="zh-CN" class="ua-windows ua-webkit">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="renderer" content="webkit">
    <meta name="referrer" content="always">
    <meta name="google-site-verification" content="ok0wCgT20tBBgo9_zat2iAcimtN4Ftf5ccsh092Xeyw" />
    <title>
豆瓣电影 Top 250
</title>

    <meta name="baidu-site-verification" content="cZdR4xxR7RxmM4zE" />
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="Sun, 6 Mar 2006 01:00:00 GMT">

这时,得到的结果只是网页的HTML 代码,还需要从中提取需要的电影名称。下面代码实现网页的内容解析:

import requests
from bs4 import BeautifulSoup 
# 通过find定位标签
# BeautifulSoup文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
def bs_parse_movies(html):
    movie_list = []
    soup = BeautifulSoup(html, "html")
    # 查找所有class属性为hd的div标签
    div_list = soup.find_all('div', class_='hd')
    # 获取每个div中的a中的span(第一个),并获取其文本
    for each in div_list:
        movie = each.a.span.text.strip()
        movie_list.append(movie) 
    return movie_list 
# css选择器定位标签
# 更多ccs选择器语法:http://www.w3school.com.cn/cssref/css_selectors.asp
# 注意:BeautifulSoup并不是每个语法都支持
def bs_css_parse_movies(html):
    movie_list = []
    soup = BeautifulSoup(html, "lxml")
    # 查找所有class属性为hd的div标签下的a标签的第一个span标签
    div_list = soup.select('div.hd > a > span:nth-of-type(1)')
    # 获取每个span的文本
    for each in div_list:
        movie = each.text.strip()
        movie_list.append(movie) 
    return movie_list 
# XPATH定位标签
# 更多xpath语法:https://blog.csdn.net/gongbing798930123/article/details/78955597
def xpath_parse_movies(html):
    et_html = etree.HTML(html)
    # 查找所有class属性为hd的div标签下的a标签的第一个span标签
    urls = et_html.xpath("//div[@class='hd']/a/span[1]") 
    movie_list = []
    # 获取每个span的文本
    for each in urls:
        movie = each.text.strip()
        movie_list.append(movie) 
    return movie_list 
def get_movies():
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
        'Host': 'movie.douban.com'
    } 
    link = 'https://movie.douban.com/top250'
    r = requests.get(link, headers=headers, timeout=10)
    print("响应状态码:", r.status_code)
    if 200 != r.status_code:
        return None 
    # 三种定位元素的方式: 
    # 普通BeautifulSoup find
    return bs_parse_movies(r.text)
    # BeautifulSoup css select
    return bs_css_parse_movies(r.text)
    # xpath
    return xpath_parse_movies(r.text) 
movies = get_movies()
print(movies)

运行程序,输出如下:

响应状态码: 200
TOP250.py:7: GuessedAtParserWarning: No parser was explicitly specified, so I'm using the best available HTML parser for this system ("lxml"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.

The code that caused this warning is on line 7 of the file TOP250.py. To get rid of this warning, pass the additional argument 'features="lxml"' to the BeautifulSoup constructor.

  soup = BeautifulSoup(html, "html")
['肖申克的救赎', '霸王别姬', '阿甘正传', '泰坦尼克号', '千与千寻', '这个杀手不太冷', '美丽人生', '星际穿越', '盗梦空间', '楚门的世界', '辛德勒的名单', '忠犬八公的故事', '海上钢琴师', '三傻大闹宝莱坞', '放牛班的春天', '机器人总动员', '疯狂动物城', '无间道', '控方证人', '大话西游之大圣娶亲', '熔炉', '教父', '触不可及', '当幸福来敲门', '寻梦环游记']

在这里插入图片描述

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值