题目:Python爬取表情包下载到本地
上面是斗图啦最新斗图的图片,我们需要的是获取每张斗图的链接,这里有两个链接需要区分以下:
src ="" 是加载后显示图的链接,是一个动态的
data-original = ""是原始的图的链接,不会改变的。所以我们选取该链接爬取
需要掌握的模块:
1 Beautiful Soup
是用Python写的一个HTML/XML的解析器,它可以很好的处理不规范标记并生成剖析树(parse tree)。 它提供简单又常用的导航(navigating),搜索以及修改剖析树的操作。它可以大大节省你的编程时间。
1.1 基本用途
- 将html文档解析成文档树,返回bs对象: BeautifulSoup(res.text,'lxml')
- 通过get_text()函数返回文档除链接、标签、段落外的文本内容
- find函数和findAll函数通过标签和属性过滤html页面,标签可以多个,属性是字典类型,自然可以多值,BeautifulSoup(res.text,'lxml').find_all('img',attrs={'class':'img-responsive lazy image_dta'}),该方法获取到的是一个列表。
- 3中的两个函数当通过keyword参数过滤时,如果key为class,则需写为class_=“green”
- bs对象可以直接调用子标签来返回,但这种方式灵活性不大,当页面结构发生些许改变后,可能会导致爬虫程序不能正确返回结果。
- 子代标签就是父标签的下一级,而后代标签是父标签下所有级别的标签
- 可以处理兄弟标签,向前处理或者向后处理,返回的列表不包括自身对象
- 为了让爬虫更稳定,最好还是让标签的选择更加的具体,意思就是尽可能多的指定属性
- 可以处理父标签
- 大多数支持字符串参数的函数,都可以使用正则表达式来实现
- 可以通过myTag.attrs返回所有属性,为字典类型
- BeautifulSoup允许把特定函数类型当做findAll函数的参数,如使用lambda表达式。唯一的限制就是这些函数必须把一个标签当做参数且返回结果是布尔类型。BeautifulSoup用这个函数来评估它遇到的每个标签对象,最后把评估结果为真的标签保留,把其他标签删除。
urlretrieve(url, filename=None, reporthook=None, data=None)函数
- 参数 finename 指定了保存本地路径(如果参数未指定,urllib会生成一个临时文件保存数据。)
- 参数 reporthook 是一个回调函数,当连接上服务器、以及相应的数据块传输完毕时会触发该回调,我们可以利用这个回调函数来显示当前的下载进度。
- 参数 data 指 post 到服务器的数据,该方法返回一个包含两个元素的(filename, headers)元组,filename 表示保存到本地的路径,header 表示服务器的响应头。
单线程:代码实现
import urllib
from bs4 import BeautifulSoup
import requests
from lxml import etree
import os
'''获取所有页面的URL'''
# BASE_PAGE_URL = "http://www.doutula.com/photo/list/?page="
# PAGE_URL_LIST = []
# for i in range(1,30):
# url = BASE_PAGE_URL + str(i)
# print (url)
'''
如何让获取任一个页面下面的图:通过requests.request来获取网页的html代码,
然后找到代码对应的src,就是每个图片对应的url
'''
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240"
url = "http://www.doutula.com/photo/list/"
def save_img(url):
filename = url.split('/')[-1]
path = os.path.join('image',filename)
img_data = urllib.request.urlretrieve(url_img,filename = path)
with requests.request('GET',url,headers = {'User-agent':ua}) as res:
content = res.text
soup = BeautifulSoup(content,'lxml')
image_list = soup.find_all('img',attrs={'class':'img-responsive lazy image_dta'})
for img in image_list:
url_img = img['data-original']
save_img(url_img)
# http = etree.HTML(content)
# res = http.xpath("//div[@class='page-content text-center']//@image")
# print(res)
多线程:代码实现
这两个黄色对于每个线程来说是全局变量。多个线程对这两个黄色区域进行多次I/O操作,所以这两个全局变量需要加锁和释放锁。
其次需要注意的是:
1 由于线程是同时进行的,也不知道具体那个快那个慢,什么时候遍历了哪几个链接。这里只能用while循环,不能用for in range的循环。
2 consumer和producer都不需要传入参数
3 每次加锁,操作结束后,一定要释放锁,不然程序会停在那里,不再执行下去
import urllib
from bs4 import BeautifulSoup
import requests
from lxml import etree
import threading
import os
'''获取所有页面的URL'''
BASE_PAGE_URL = "http://www.doutula.com/photo/list/?page="
PAGE_URL_LIST = []
for i in range(1,30):
url_page = BASE_PAGE_URL + str(i)
PAGE_URL_LIST.append(url_page)
IMG_URL = []
'''
如何获取任一个页面下面的图:通过requests.request来获取网页的html代码,
然后找到代码对应的src,就是每个图片对应的url
'''
gLock = threading.Lock()
def producer():
while True:
gLock.acquire()
global PAGE_URL_LIST
if len(PAGE_URL_LIST) ==0:
gLock.release()
break
else:
page_url = PAGE_URL_LIST.pop()
gLock.release()
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240"
with requests.request('GET',page_url,headers = {'User-agent':ua}) as res:
content = res.text
soup = BeautifulSoup(content,'lxml')
image_list = soup.find_all('img',attrs={'class':'img-responsive lazy image_dta'})
gLock.acquire()
for img in image_list:
url_img = img['data-original']
IMG_URL.append(url_img)
gLock.release()
def consumer():
while True:
gLock.acquire()
global IMG_URL
if len(IMG_URL)==0:
gLock.release()
continue
else:
face_url = IMG_URL.pop()
gLock.release()
filename = face_url.split('/')[-1]
path = os.path.join('image',filename)
img_data = urllib.request.urlretrieve(face_url,filename = path)
def main():
'''全局变量是每个图片对应的URL地址'''
###建立3个线程爬取图片的URL地址(生产者模式)
for x in range(3):
th1 = threading.Thread(target=producer)
th1.start()
###建立5个线程保存爬取到URL地址对应的图片(消费者模式)
for y in range(5):
th2 = threading.Thread(target=consumer)
th2.start()
if __name__ == "__main__":
main()