Python网络爬虫requests、bs4爬取空姐网图片

如之前的几篇文章(Python爬虫框架之Scrapy详解Python爬虫框架Scrapy之爬取糗事百科大量段子数据),使用了Scrapy框架并且爬取了糗事百科的段子存入MongoDB中。

Scrapy框架很好,也提供了很多扩展点,可以自己编写中间件处理Scrapy的Request和Response。但是可定制化或者可掌控性来说,还是自己写的爬虫更加强一些。

如果写简单更加可控的爬虫,还是建议使用Python第三方库:requests和bs4。

###requests和bs4爬空姐网图片

####requests

requests是Python非常流行的处理网络数据的第三方库。相对于Python内置框架urllib、urllib2来说,requests提供的操作更加简洁而且更加丰富。如requests示例:

#HTTP请求类型
#get类型
r = requests.get('https://github.com/timeline.json')
#post类型
r = requests.post("http://m.ctrip.com/post")
#put类型
r = requests.put("http://m.ctrip.com/put")
#delete类型
r = requests.delete("http://m.ctrip.com/delete")
#head类型
r = requests.head("http://m.ctrip.com/head")
#options类型
r = requests.options("http://m.ctrip.com/get")

#获取响应内容
print r.content #以字节的方式去显示,中文显示为字符
print r.text #以文本的方式去显示

#URL传递参数
payload = {'keyword': '日本', 'salecityid': '2'}
r = requests.get("http://m.ctrip.com/webapp/tourvisa/visa_list", params=payload) 
print r.url #示例为http://m.ctrip.com/webapp/tourvisa/visa_list?salecityid=2&keyword=日本

#获取/修改网页编码
r = requests.get('https://github.com/timeline.json')
print r.encoding
r.encoding = 'utf-8'

#json处理
r = requests.get('https://github.com/timeline.json')
print r.json() #需要先import json    

#定制请求头
url = 'http://m.ctrip.com'
headers = {'User-Agent' : 'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 4 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19'}
r = requests.post(url, headers=headers)
print r.request.headers

#复杂post请求
url = 'http://m.ctrip.com'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload)) #如果传递的payload是string而不是dict,需要先调用dumps方法格式化一下

#post多部分编码文件
url = 'http://m.ctrip.com'
files = {'file': open('report.xls', 'rb')}
r = requests.post(url, files=files)

#响应状态码
r = requests.get('http://m.ctrip.com')
print r.status_code
    
#响应头
r = requests.get('http://m.ctrip.com')
print r.headers
print r.headers['Content-Type']
print r.headers.get('content-type') #访问响应头部分内容的两种方式
    
#Cookies
url = 'http://example.com/some/cookie/setting/url'
r = requests.get(url)
r.cookies['example_cookie_name']    #读取cookies
    
url = 'http://m.ctrip.com/cookies'
cookies = dict(cookies_are='working')
r = requests.get(url, cookies=cookies) #发送cookies

#设置超时时间
r = requests.get('http://m.ctrip.com', timeout=0.001)

#设置访问代理
proxies = {
           "http": "http://10.10.10.10:8888",
           "https": "http://10.10.10.100:4444",
          }
r = requests.get('http://m.ctrip.com', proxies=proxies)

通过requests,我们可以很方便的发送GET、POST、DELETE、PUT请求,获取相应数据等等。

####bs4

bs4是指BeautifulSoup 4.x版本。相对于BeautifulSoup 2.x和3.x,4.x提供了更加丰富和人性化的api。使用BeautifulSoup,我们可以很方便的定位到HTML中我们想要的元素,获取元素值等等。如:

soup.title
# <title>The Dormouse's story</title>

soup.title.name
# u'title'

soup.title.string
# u'The Dormouse's story'

soup.title.parent.name
# u'head'

soup.p
# <p class="title"><b>The Dormouse's story</b></p>

soup.p['class']
# u'title'

soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

soup.find_all('a')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.find(id="link3")
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

###查看空姐网网页结构

首先,我们查看一下空姐网的网页结构,找到每个人的相册页面。在kongjie.com里面随意翻翻,就能找到热门相册页面,如图:

首先,分析一下该页面结构,提取出每个人的相册页链接。如图:

网页结构

class属性为ptw的div下,ul中的每一个li都是每个人的相册封面,通过提取li中的链接,就能进入每个人的相册。

###开始爬取

####提取相册链接

从上面这个页面提取每个人相册链接的css表达式为div.ptw li.d。这样,我们就可以把这个表达式用在BeautifulSoup里面了。

如,

def parse_album_url(url):
    """
    解析出相册url,然后进入相册爬取图片
    """
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.text, 'lxml')
    people_list = soup.select('div.ptw li.d')
    for people in people_list:
        save_images_in_album(people.div.a['href'])

    # 爬取下一页
    next_page = soup.select_one('a.nxt')
    if next_page:
        parse_album_url(next_page['href'])

if __name__ == '__main__':
    parse_album_url(start_url)

现在,获取到了每个人的相册链接,接下来就是编写save_images_in_album()方法,进入每个人的相册里面抓取图片了。再提一下,再这里,我们提取完一页中每个人的相册链接之后,解析了网页里的“下一页”的链接,这样就能自动翻页抓取了。“下一页”链接的网页结构如下:

下一页

通过css表达式a.nxt就能提取到这个下一页链接。

####进入相册提取图片

要编写爬虫,我们还是获取一下浏览器访问这个页面时的Request Headers,这样,就能绕过一些简单的反爬虫手段。

然后,进入到相册内部,查看一下网页结构,如图:

相册图片列表

我们得知,id为photo_pic,class为c的div里面,第一个超链接里面的img标签就是大图。所以,我们在这里提取这个链接。我们使用soup.find('div', id='photo\_pic', class\_='c')定位到id为photo_pic、class为c的div,然后通过image_div.a.img[‘src’]就能拿到这个图片的链接了。

拿到一张图片的链接之后,我们需要切换到下一张图片。可以看到,大图下面最后面有个向右的箭头,这个是下一张图的按钮,我们获取这个按钮的链接,获取连接对应的css表达式为div.pns.mlnv.vm.mtm.cl a.btn[title="下一张"],然后就可以重复上面两个步骤爬取相册里面所有的照片了。

####图片去重

在这里,我们怎么知道一个相册里的图片都爬取完成了呢?

我们使用redis来存放爬取了的图片id,如果一张图片id已存在redis中了,那么不爬取了,这样,我们就能很方便的知道一个相册是否爬取完了(相册中所有图片id都存在redis中了,就表示该相册爬取完了)。

因此,我们写出save_images_in_album()方法如下:

def save_images_in_album(album_url):
    """
    进入空姐网用户的相册,开始一张一张的保存相册中的图片。
    """
    # 解析出uid和picid,用于存储图片的名字
    uid_picid_match = uid_picid_pattern.search(album_url)
    if not uid_picid_match:
    	return
    else:
        uid = uid_picid_match.group(1)
        picid = uid_picid_match.group(2)

    response = requests.get(album_url, headers=headers)
    soup = BeautifulSoup(response.text, 'lxml')
    image_div = soup.find('div', id='photo_pic', class_='c')
    if image_div and not redis_con.hexists('kongjiewang', uid + ':' + picid):
        image_src = domain_name + image_div.a.img['src']
        save_img(image_src, uid, picid)
        redis_con.hset('kongjie', uid + ':' + picid, '1')

    next_image = soup.select_one('div.pns.mlnv.vm.mtm.cl a.btn[title="下一张"]')
    if not next_image:
    	return
    # 解析下一张图片的picid,防止重复爬取图片,不重复则抓取
    next_image_url = next_image['href']
    next_uid_picid_match = uid_picid_pattern.search(next_image_url)
    if not next_uid_picid_match:
    	return
	next_uid = next_uid_picid_match.group(1)
    next_picid = next_uid_picid_match.group(2)
    if not redis_con.hexists('kongjie', next_uid + ':' + next_picid):
        save_images_in_album(next_image_url)

这里,我们从相册的url中,通过正则表达式:uid_picid_pattern = re.compile(r’.*?uid=(\d+).*?picid=(\d+).*?’)解析出用户id和每张图片的id。然后就可以用redis来去重了。

####下载图片

在上面这个函数里,我们拿到了每张图片大图的链接,即image_src变量。然后我们就可以编写save_img()方法来保存图片了。如:

def save_img(image_url, uid, picid):
    """
    保存图片到全局变量save_folder文件夹下,图片名字为“uid_picid.ext”。
    其中,uid是用户id,picid是空姐网图片id,ext是图片的扩展名。
    """
    try:
        response = requests.get(image_url, stream=True)
        # 获取文件扩展名
        file_name_prefix, file_name_ext = os.path.splitext(image_url)
        save_path = os.path.join(save_folder, uid + '_' + picid + file_name_ext)
        with open(save_path, 'wb') as fw:
            fw.write(response.content)
        print uid + '_' + picid + file_name_ext, 'image saved!', image_url
    except IOError as e:
        print 'save error!', e, image_url

###运行结果

最后,我们在命令行运行Python kongjiewang.py。看一下结果:

运行结果

大功告成!

感兴趣的可以关注:

github地址
喜欢的可以关注微信公众号:

这里写图片描述

##参考

  1. 我自己的头条号:Python爬虫框架Scrapy之爬取糗事百科大量段子数据
发布了14 篇原创文章 · 获赞 37 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/c315838651/article/details/72773602