Python数据采集与爬虫大数据 爬虫Python AI Sql

【爬虫】爬虫系列一之静态页面的获取

2017-06-02  本文已影响237人  whaike

许久未更,来一篇防呆。
本次主要说说使用python语言去抓取静态页面的方法,很基础的东西,总结给自己看

学前准备

环境:windows7系统 、Python2.7语言、requests库、lxml库
环境安装中python2.7就不说了,requests库使用pip安装即可,lxml因为是C语言编写,所以安装前需要单独下载一个VCforPython.exe去弥补一个C库,之后再使用pip进行安装或者下载whl文件安装都可以。

爬虫其实就是用代码的方式去模拟人类操作浏览器,然后获取数据,最简单的爬虫其实算不上爬虫,只能算是一些与网络相关的脚本而已。

使用python做爬虫,起初用的是urllib和urllib2这两个自带的库,但是这两个库并没有那么好用,一个请求都要好几行代码,这不符合懒人的原则,直到发现requests库,简直是神作,安装一个requests库pip install requests,使用的时候直接引入即可。

import requests

html = requests.get(url).text 

一行代码就可以获取URL的网页源代码,第一次使用真是激动到不行。
当然还有很多其他的用法,常用的是get、post方法,参数为url、params(get方式传参)、data(post方式传参)、timeout(超时)、headers(请求头)。参考requests中文文档

请求一张网页的时候,最简单的代码如上以写,但是很多时候网站都是不愿意配合的,反扒的强度也有区别,其中相当多的一部分都会对请求头所携带的参数进行检查,现今的浏览器基本上都自带了抓取网络请求的工具,很多时候不必要去额外的下载其他的工具进行抓包分析,在浏览器里,可以通过开发者工具(F12)去查看网页的请求(有的浏览器,不同兼容模式的显示方式也有区别)。在network栏可以看到网页向服务器请求的数据.

Paste_Image.png
最上一栏中,Elements中显示的是网页的源代码,是渲染后的源代码,包括JS渲染的部分,但是如果使用requests去获取的话,并不会得到js渲染后的部分,而只会得到在网页中右键-查看源码里看到的分离了js渲染的原始代码。

Network中就是浏览器向服务器发起的请求以及请求到的数据的列表,也就是抓包工具所捕获的数据包。
说到这里,不得不看一看截图中第二排工具,红色表示打开抓取数据开关,如果点击,变成灰色则浏览器在请求数据的时候,其过程不会被记录(不会进行抓包)。第二个按钮是清空记录,<b>第四个是过滤开关,如果你看不到第三行的工具栏,则通过此按钮打开即可</b>。过滤栏(第三行工具)中ALL表示所有记录,XHR一般显示的是json数据等使用XMLHttpRquest动态(异步)请求的数据,js栏可以显示所有js文件,其他以此类推。最前面的搜索框提供对所有抓取过的包按名字的模糊搜索。
网页未渲染的初始源代码一般在doc中可以查看。当然有的动态获取的也会在XHR栏显示,以后再说。

下方name显示所有查询出来的记录,点击某个记录,在右边即可看到关于这个请求的所有信息。常见的如下:

Headers中,General表示总览,Remote Address:表示请求的网页的服务器的IP地址。Request URL:请求的地址。Request Method:网页请求方式,一般为GET或者POST方式,Status Code:网页请求的状态码,一般请求成功是200,如果是302则表示网页重定向(就是在服务端发现重定向需求,所请求的数据在另一个URL中,并且自动连接到那个URL),如果是304 Not Modified则表示该请求所需的数据是从本地缓存中直接获取的,并未发生网络请求。这种情况一般发生在刷新网页的时候,一部分的数据已经保存到了本地,那么这种直接从本地提取数据的方式就可以减少网络请求,加快刷新速度,所以这些数据也不会得到更新,一般用于不会变化的信息。404表示请求的网页不存在。其他参考HTTP状态码 - 百度百科

Response Headers:请求应答头(服务器返回的数据)
Request Headers:请求头(发送给服务器的数据)
Query String Parameters:请求参数(发送给服务器的数据,以说明具体的需求)

网页返回的请求应答头通常不需要关注,在这里我们着重关注下后两个。
Request Headers,请求头,这里可以看到浏览器发起某个请求所携带的参数,其中
accept:表示浏览器(客户端)可以接受的数据格式,通常有xml、text、json等。accept-encoding:客户端可接收的网页数据的压缩方式。
accept-language:可接收的语言。
referer:引用,表示网页请求的来源,即我是从哪一个页面链接过来的,这个参数通常应用在反扒中,不可大意。
user-agent:记录客户端(浏览器)的版本及客户端的环境等,所以只要你发起请求,服务器就可以知道你用的什么浏览器什么操作系统哪个IP地址什么时候访问等等信息。
Cookie:是cookies信息,用于客户端的身份验证,模拟请求的时候会用到这个。
Host:用于将同一个请求 发送到不同的服务器,即虚拟web服务器或者说是为了实现负载均衡。
详细参考HTTP 请求头 - 百度百科

右侧面板除了Headers以外,preview和Response中可以看到请求体,也就是客户端所需要请求的数据,只不过在Response中看到的是数据本来的格式,而在preview中是格式化之后的格式,这点在返回的数据为json时尤为明显。

我们在使用request构建请求头的时候,所添加的headers就是Request Headers中的内容,如果不自己添加headers,那么requests或者其他库在向服务器请求数据的时候也许就会使用默认的headers数据甚至不添加headers,这样服务器就很容易发现并封锁你的请求。

使用requests请求一张页面

如上所述,我们打开漫漫看电影大全首页,调出开发人员工具(F12),刷新页面,在doc过滤器中可以看到网页最初的源代码,如下

Paste_Image.png

在headers栏可以看到请求头,General中可以看到请求地址和请求方式。

Paste_Image.png

在这里我们要请求获取漫漫看的首页,那么请求头可以使用headers中的部分内容,为了简单,我将Request Headers中的内容拷贝出来,删除cookies部分(没有登录信息,可以不使用cookies),手动格式化之后,以python字典的方式保存到变量,所以请求一张漫漫看首页的代码是这样的

#-*- coding:utf-8 -*-
import requests
# from lxml import etree
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

headers = {
    "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding":"gzip, deflate, sdch",
    "Accept-Language":"zh-CN,zh;q=0.8",
    "Cache-Control":"max-age=0",
    "Connection":"keep-alive",
    "Host":"www.manmankan.com",
    "Referer":"http://www.manmankan.com/dy2013/",
    "Upgrade-Insecure-Requests":"1",
    "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
}

def getHtml():
    url = 'http://www.manmankan.com/dy2013/dianying/'
    res = requests.get(url,headers=headers,timeout=30).text
    return res


if __name__ == '__main__':
    html = getHtml()
    print html

但是打印出来的网页是乱码的,这里我们在网页源代码的head标签里发现指定网页的编码方式的代码段<meta http-equiv="Content-Type" content="text/html; charset=gb2312">,所以将获取到的网页进行一次转码,修改部分如下

res = requests.get(url,headers=headers,timeout=30)
res.encoding='gb2312' #修改网页的编码方式
res = res.text

拿到网页之后,可以使用lxml库进行页面解析。
例如,如果我们想拿到最新100部电影,就可以在最新100部电影区域右键,审查元素,就可以看到该部分的源代码。

Paste_Image.png
可以发现,所有的电影都在一个div里面,每个小版块一个ul区分,那么使用xpath语法(w3school - xpath语法教程)即可获取该部分,XPath的路径图简单的话可以直接右键进行复制, Paste_Image.png

代码如下

tree = etree.HTML(res)
uls = tree.xpath('//*[@id="m_cont_1"]/ul')
print len(uls) #这里就可以看到输出了10个分类

前端知识这里就不说了,通过xpath及前端基础即可拿到所有的电影,如下:

#-*- coding:utf-8 -*-
import requests
from lxml import etree
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

headers = {
    "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding":"gzip, deflate, sdch",
    "Accept-Language":"zh-CN,zh;q=0.8",
    "Cache-Control":"max-age=0",
    "Connection":"keep-alive",
    "Host":"www.manmankan.com",
    "Referer":"http://www.manmankan.com/dy2013/",
    "Upgrade-Insecure-Requests":"1",
    "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
}

def getHtml():
    url = 'http://www.manmankan.com/dy2013/dianying/'
    res = requests.get(url,headers=headers,timeout=30)
    res.encoding='gb2312' #修改编码方式
    res = res.text #获取请求体
    return res

def getTop100(res):
    tree = etree.HTML(res)
    lis = tree.xpath('//*[@id="m_cont_1"]/ul/li')
    movies = []
    for li in lis:
        name = li.xpath('div[last()]/a/text()')[0] #电影名称
        url = 'http://www.manmankan.com'+ li.xpath('div[last()]/a/@href')[0] #电影链接等于主页地址+解析到的地址
        movies.append((name,url))
    return movies

if __name__ == '__main__':
    html = getHtml()
    movies = getTop100(html)
    print len(movies) #电影总数
    for m in movies:
        print m[0],m[1]

Paste_Image.png

此时,我们就已经拿到了100个电影名称和其链接。

<b>下钻到第二层链接</b>
接下来,我们要进入这些链接去获取每个电影的具体的上映时间及地区。此模块单独封装一个函数即可。

#解析上映时间和地区
def findTAndC(res):
    tree = etree.HTML(res)
    ul = tree.xpath('/html/body/div[3]/div[2]/div[1]/div[1]/div[2]/ul')[0]
    try:
        pub_time = ul.xpath('li[2]/text()')[0] #上映时间
        pub_add = ul.xpath('li[4]/a/text()')[0] #地区
    except Exception: #如果没有上映,则更新解析路径(这里的路径不完全正确,部分时间或地区也拿不到,只是举个例子并未深入)
        pub_time = tree.xpath('//*[@id="isryz"]/text()')[0] #正在热映的时间
        pub_add = ul.xpath('li[2]/a/text()')[0] #地区
    return (pub_time,pub_add)

至于传参就不用说了吧,使用requests获取网页源码传进去就好.

简单的数据保存方式

使用codecscsv模块可以将数据保存到csv文件(<b>可以使用Excel打开进行编辑</b>)。
使用json模块可以将数据很友好的保存到json文件。

下面我们使用codecscsv模块将数据保存到csv文件,完整代码如下:

#-*- coding:utf-8 -*-
import requests
from lxml import etree
import csv
import codecs
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

headers = {
    "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding":"gzip, deflate, sdch",
    "Accept-Language":"zh-CN,zh;q=0.8",
    "Cache-Control":"max-age=0",
    "Connection":"keep-alive",
    "Host":"www.manmankan.com",
    "Referer":"http://www.manmankan.com/dy2013/",
    "Upgrade-Insecure-Requests":"1",
    "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"
}

#根据url获取网页源码
def getHtml(url):
    res = requests.get(url,headers=headers,timeout=30)
    res.encoding='gb2312'
    res = res.text
    return res

#提取Top100电影
def getTop100(res):
    tree = etree.HTML(res)
    lis = tree.xpath('//*[@id="m_cont_1"]/ul/li')
    movies = []
    for li in lis:
        name = li.xpath('div[last()]/a/text()')[0] #电影名称
        url = 'http://www.manmankan.com'+ li.xpath('div[last()]/a/@href')[0] #电影链接等于主页地址+解析到的地址
        movies.append((name,url))
    return movies

#解析上映时间和地区
def findTAndC(res):
    tree = etree.HTML(res)
    ul = tree.xpath('/html/body/div[3]/div[2]/div[1]/div[1]/div[2]/ul')[0]
    try:
        pub_time = ul.xpath('li[2]/text()')[0] #上映时间
        pub_add = ul.xpath('li[4]/a/text()')[0] #地区
    except Exception: #如果没有上映,则更新解析路径(这里的路径不完全正确,部分时间或地区也拿不到,只是举个例子并未深入)
        pub_time = tree.xpath('//*[@id="isryz"]/text()')[0] #正在热映的时间
        pub_add = ul.xpath('li[2]/a/text()')[0] #地区
    return (pub_time,pub_add)

#保存数据到文件
title = False #false表示未给csv文件添加标题栏,true表示已经添加
def saveToCsv(row):
    global title
    with codecs.open('movies.csv','ab') as f:
        f.write(codecs.BOM_UTF8)
        w = csv.writer(f)
        if not title:
            w.writerow([u'电影名称',u'电影链接',u'上映时间',u'上映地区'])
            title = True
        w.writerow(row)

if __name__ == '__main__':
    html = getHtml('http://www.manmankan.com/dy2013/dianying/')
    movies = getTop100(html)
    print len(movies) #电影总数
    for m in movies:
        print m[0],m[1],
        pub_time,pub_add = findTAndC(getHtml(m[1]))
        print pub_time,pub_add
        row = (m[0],m[1],pub_time,pub_add)
        saveToCsv(row)

静态网页的解析,大抵如此。有新的想法,再来更新。

上一篇 下一篇

猜你喜欢

热点阅读