Python简单爬取网页图片

网页数据的采集及相关库
  • 在python中我们通过网络库进行数据的采集和下载。


    网络库.png

​ urllib库、requests库,他们俩是http协议常用库

​ BeautifulSoup库,它是xml格式处理库

from urllib import request
url = "http://www.baidu.com"
response = request.urlopen(url,timeout=2)
print(response.read().decode('utf-8'))
  • 在python中字符串和字节之间的相互转换是通过encode()、decode()、bytes()、str()这些函数实现的。Python3中严格区分了字符串和字节的概念,在网络的底层传输的文字就是字节形式,而上层是字符串形式,我们”看得懂“的是字符串,但是很多底层函数需要用到字节形式。就必然产生了字符串和字节之间相互转换的需求,那么上面提到的几个函数就是用来做字符串和字节之间转换的。

​ 如我们需要字符串转换成字节,可以使用bytes()或encode()进行转换:

s='你好'
b1=s.encode('utf-8') # 使用utf8编码将字符串转换为字节
b2=bytes(s, encoding='utf-8') #和上面功能一样

​ 将字节转换回字符串:

b1.decode('utf-8')
str(b2, encoding='utf-8')

​ 这就是这几个函数之间的区别

网页常用的两种请求方式get和post

http://httpbin.org/是一个网络请求的网站

from urllib import request
from urllib import parse
# get请求和post请求
getresponse = request.urlopen("http://httpbin.org/get",timeout=1)
print(getresponse.read().decode('utf-8'))

data = bytes(parse.urlencode({'world':'hello'}),encoding='utf-8')
postresponse = request.urlopen("http://httpbin.org/post",data=data)
print(postresponse.read().decode('utf-8'))

在网络请求中一般需要设置超时时间timeout,否则当发生超时会一直卡住。我们通过设置timeout= 0.1s来模拟超时,并通过try捕获异常

import socket
import urllib
try:
    response2 = request.urlopen("http://httpbin.org/get",timeout=0.1)
    print(response2.read().decode('utf-8'))
except urllib.error.URLError as e:
    # 判断异常的原因是不是socket超时导致的
    if isinstance(e.reason,socket.timeout):
        print("time out")
http头部信息的模拟

当我们使用urllib库进行网络信息的请求时,有时会被拒绝。这是因为网站为了防止用户恶意获取数据,增加了一些验证,主要是验证我们的请求是不是一个标准的浏览器。当我们使用urllib请求和浏览器请求时,请求的头部信息headers是有区别的。urllib的headers客户端信息是"User-Agent": "Python-urllib/3.6",而浏览器的headers中客户端信息是"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"

所以我们在提交时不仅可以在提交我们的url请求,同时还可以提交一份我们自己模拟的http头部信息。

from urllib import request, parse

url = 'http://httpbin.org/post'

headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
# "Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.8",
"Connection": "close",
"Cookie": "_gauges_unique_hour=1; _gauges_unique_day=1; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1",
"Referer": "http://httpbin.org/",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER"
}

dict = {
'name': 'value'
}

data = bytes(parse.urlencode(dict), encoding='utf8')
req = request.Request(url=url, data=data, headers=headers, method='POST')
response = request.urlopen(req)
print(response.read().decode('utf-8'))

注:上面的示例中注释掉了"Accept-Encoding": "gzip, deflate",否则会报如下错:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte

首先 HTTP Header中Accept-Encoding 是浏览器发给服务器,声明浏览器支持的编码类型 ,就是定义了客户端和浏览器传输传输”文本内容“时是否需要压缩,而gzip, deflate就是客户端和服务端通用的压缩算法。为什么会出现上面的UnicodeDecodeError的错误呢?是因为Python默认按照utf-8的编码读取网页文件时,发现是乱码(因为被压缩过了),所以报错了。就像是一个txt的文本文件如果被rar压缩软件压缩过,再用记事本打开是乱码是同样的道理。所以结论就是要根据服务端的网页编码确定是否需要进行 'Accept-Encoding':' gzip, deflate' 的解压缩操作。

Requests库的基本使用

requests是对urllib封装的第三方库,方便我们进行get和post的网络请求。

  • 安装方式

    也是需要通过pip来进行安装,pip install requests

  • requests进行网络请求

    import requests
    
    # get请求
    url = 'http://httpbin.org/get'
    data = {'key': 'value', 'abc': 'xyz'}
    # .get是使用get方式请求url,字典类型的data不用进行额外处理
    response = requests.get(url,data)
    print(response.text)
    
    # post请求
    url = 'http://httpbin.org/post'
    data = {'key': 'hello', 'text': 'world'}
    # .post表示为post方法
    response = requests.post(url,data)
    # 返回类型可以为text,也可以为json格式
    print(response.json())
    

Request结合正则表达式爬取图片链接
import requests
import re

content = requests.get('http://www.cnu.cc/discoveryPage/hot-人像').text
# print(content)

# <div class="grid-item work-thumbnail">
# <a href="http://www.cnu.cc/works/329928" class="thumbnail" target="_blank">
# <div class="title">四十七</div>
# <div class="author">拍照的古德卡特</div>

# re.S叫做单行模式,就是你用正则要匹配的内容在多行里,会增加你要匹配的难度,这时候使用re.S把每行最后的换行符\n当做正常的一个字符串来进行匹配的一种小技巧
pattern = re.compile(r'<a href="(.*?)".*?title">(.*?)</div>',re.S)
results = re.findall(pattern,content)
print(results)
for result in results:
    url,name = result
    # '\s'匹配空白的字符,在utf-8编码下,换行符 空格等都可以匹配
    print(url,re.sub('\s','',name))
Beautiful Soup的安装和使用
  • 安装

    通过pip安装:pip install bs4

  • 示例

    html_doc = """
    <html><head><title>The Dormouse's story</title></head>
    <body>
    <p class="title"><b>The Dormouse's story</b></p>
    
    <p class="story">Once upon a time there were three little sisters; and their names were
    <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
    <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
    and they lived at the bottom of a well.</p>
    
    <p class="story">...</p>
    """
    
    from bs4 import BeautifulSoup
    
    # 指定以'xml'的格式来解析
    soup = BeautifulSoup(html_doc,'lxml')
    # 通过soup.prettify()来对混乱的格式进行格式化处理
    print(soup.prettify())
    
    # 找到title标签
    # print(soup.title)
    
    # title 标签里的内容
    # print(soup.title.string)
    
    # 找到p标签
    print(soup.p)
    
    # 找到p标签class的名字 soup.p['class']默认取第一个
    print(soup.p['class'])
    
    # 找到第一个a标签
    # print(soup.a)
    
    # 找到所有的a标签
    # print(soup.find_all('a'))
    
    # 找到id为link3的的标签
    print(soup.find(id="link3"))
    
    # 找到所有<a>标签的链接
    # for link in soup.find_all('a'):
    #     print(link.get('href'))
    
    # 找到文档中所有的文本内容
    # print(soup.get_text())
    

    当提示bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library?报错时,需要通过pip install lxml 安装lxml解决

使用爬虫爬取新闻网站

爬取百度新闻网页内容

from bs4 import BeautifulSoup
import requests

headers = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8",
    "Connection": "close",
    "Cookie": "_gauges_unique_hour=1; _gauges_unique_day=1; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1",
    "Referer": "http://www.infoq.com",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER"
}

# url = 'http://www.infoq.com/cn/news'
url = 'http://news.baidu.com/'

def craw(url):
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.text, 'lxml')

    for hotnews in soup.find_all('div', class_='hotnews'):
        for news in hotnews.find_all('a'):
            print(news.text,end=' ')
            print(news.get('href'))

# 获取新闻标题
craw(url)
使用爬虫爬取图片链接并下载图片
from bs4 import BeautifulSoup
import requests
import os
import shutil

headers = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8",
    "Connection": "close",
    "Cookie": "_gauges_unique_hour=1; _gauges_unique_day=1; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1",
    "Referer": "http://www.infoq.com",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 LBBROWSER"
}

url = 'http://www.infoq.com/cn/presentations'

# 取得图片
def craw3(url):
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.text, 'lxml')
    for pic_href in soup.find_all('div', class_='news_type_video'):
        for pic in pic_href.find_all('img'):
            imgurl = pic.get('src')
            dir = os.path.abspath('.')
            # 通过os.path.basename()只取图片url地址后面的 xx.jpg名字
            filename = os.path.basename(imgurl)
            imgpath = os.path.join(dir, filename)
            print('开始下载 %s' % imgurl)
            download_jpg(imgurl, imgpath)

# 下载图片
# Requests 库封装复杂的接口,提供更人性化的 HTTP 客户端,但不直接提供下载文件的函数。
# 需要通过为请求设置特殊参数 stream 来实现。当 stream 设为 True 时,
# 上述请求只下载HTTP响应头,并保持连接处于打开状态,
# 直到访问 Response.content 属性时才开始下载响应主体内容
def download_jpg(image_url, image_localpath):
    response = requests.get(image_url,stream = True)
    if response.status_code == 200:
        with open(image_localpath,'wb') as f:
            response.raw.decode_content = True
            shutil.copyfileobj(response.raw,f)

# 翻页
j = 0
for i in range(12, 37, 12):
    url = 'http://www.infoq.com/cn/presentations' + str(i)
    j += 1
    print('第 %d 页' % j)
    craw3(url)
主流爬虫框架

python爬虫框架非常多,比较流行主要有Scrapy、PySpider。Scrapy因为有XPath和CSS选择器,从个人使用习惯更好用一些。pyspider更简单,上手较快。还有值得学习的有urllib2、urllib3、selenium这些包,简单的爬虫用urllib就可以实现了。selenium可以调用浏览器,完整的解析js,执行速度上要慢,这些都是编写爬虫常用的包

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容