一直想写爬虫,也想学python,最近看了慕课网爬虫的视频, 就自己试着写了个爬虫。单线程爬取,效率很慢,谢谢百度没有封IP 哈哈哈。
我是win7 64位, python3.4,还用到了urllib, BeautifulSoup,
python2.7用不了, 最主要就是urllib 和print 有点不同吧。
我的思路是: 利用firefox的网页分析工具。
1.找出当前贴吧首页所有标题的地址(一页应该是五十个),把地址截取下来放在set里面。
外标题的地址格式:http://tieba.baidu.com/p/4220129198 ,所以利用BeautifulSoup可以轻易得到内容。
那么该主题的地址就是 http://tieba.baidu.com/p/4220129198 ,字符串拼接一下即可。
2.遍历刚才所有的标题地址,进入该主题,收集图片地址,如果有分页,进入分页,挨着收集到最后一页。
分页还是上面的那样也是用的pn,所以直接找到尾页(<a href="/p/4220129198?pn=90">尾页</a>),然后循环从pn=2遍历到尾页(pn=90)即可。
2.1 访问刚才收集到的所有图片地址,下载。
3.遍历完第一页所有标题地址后,进入第二页(#url='http://tieba.baidu.com/f?%s&ie=utf-8&pn=2'%(urltbname)),再继续重复1,2步骤。
直到最后一页。(但是这个过程很长, 所以除了很少贴的贴吧, 我并没有爬完一个帖子数比较多的贴吧)
代码贴在下面,如果对你有帮助,很开心。
相当一部分的网页解析代码没有提取出来。。
spider_main.py
#coding:UTF8
'''
Created on 2016年1月25日
@author: thewindkee
'''
from tieba import tieba_picdownloader,tieba_htmldownloader,tieba_parser,url_manager
import urllib.request
from bs4 import BeautifulSoup
import re
import os
from urllib import parse
import time
class SpiderMain(object):
def __init__(self):
self.piclinks=set()
self.urls = url_manager.UrlManager()
self.downloader = tieba_htmldownloader.HtmlDownloader()
self.parser = tieba_parser.HtmlParser()
self.picdownloader=tieba_picdownloader.PicDownloader()
#用于统计总图片数,和爬取过的主题数,总网页数
self.piccount=0
self.titlecount=0
self.pagecount=0
def __crawl__(self,tiebaname):
folderpwd=os.getcwd()+'\%s'%(tiebaname)
urltbname=parse.urlencode({'kw':'%s'%(tiebaname)})
#创建以贴吧名为名的文件夹
if not os.path.exists(folderpwd):
os.makedirs(tiebaname)
os.chdir(folderpwd)
baseurl='http://tieba.baidu.com'
#/f?kw=kw=%E5%9B%9B%E5%B7%9D%E5%A4%A7%E5%AD%A6&ie=utf-8&pn=0
url='http://tieba.baidu.com/f?%s&ie=utf-8'%(urltbname)
#开始得到外页所有标题地址
#<a class="last" href="/f?kw=%E5%9B%9B%E5%B7%9D%E5%A4%A7%E5%AD%A6&ie=utf-8&pn=670700">尾页</a>
tiebacont=self.downloader.download(url)
soup=BeautifulSoup(tiebacont,'html.parser',from_encoding='utf-8')
#得到贴吧最后一页的pn值
lastpagenum=self.parser._get_outermaxpage(tiebacont)
print(lastpagenum)
while self.titlecount<=lastpagenum:
url=url+'&pn=%s'%(self.titlecount)
print('当前url=',url)
tiebacont=self.downloader.download(url)
soup=BeautifulSoup(tiebacont,'html.parser',from_encoding='utf-8')
titlelinks=soup.find_all('a',href=re.compile(r"/p/d+"),class_="j_th_tit")
#用dataurls装外标题地址
dataurls=set()
for link in titlelinks:
dataurls.add(urllib.parse.urljoin(baseurl,link['href']))
print(dataurls)
#开始爬取当前页所有主题
while dataurls.__len__()>0:
self.titlecount+=1
self.pagecount+=1
dataurl=dataurls.pop()
#爬取每个标题内容
cont=self.downloader.download(dataurl)
if cont is None:
continue
self.piclinks=self.parser._get_piclinks(cont)
# for piclink in self.piclinks:
# print('打印piclink',piclink)
#分页图片链接收集
#得到最后一页的pn值
maxpage=self.parser._get_innermaxpage(cont)
#把urlnum提取出来,每个主题对应一个/p/urlnum
ithr=re.finditer(r"d+", dataurl)
urlnum=ithr.__next__().group()
if maxpage !=0:
print('组装并进入分页')
#http://tieba.baidu.com/p/3844xx489x?pn=2
for i in range(2,maxpage+1):
li=['http://tieba.baidu.com/p/',urlnum,'?pn=',str(i)]
nextpageurl=''.join(li) #下一页的url
print('nextpageurl=',nextpageurl)
cont=self.downloader.download(nextpageurl)
if cont is None:
continue
newpiclinks=self.parser._get_piclinks(cont)
self.piclinks=self.piclinks.union(newpiclinks);
self.pagecount+=1
#开始下载该主题的全部图片,保存在urlnum目录下
i=0;
for piclink in self.piclinks:
i+=1
folder=urlnum
savename=''.join([urlnum,'n',str(i)])
savetype='.jpg'
print('图片地址=%s'%(piclink))
self.picdownloader.save(piclink,folder,savename,savetype)
self.piccount+=1
# self.picdownloader.save(piclink,folder,str(count),savetype)
self.piclinks.clear()
print('目前总共爬取过%d个主题,%d个网页,下载了%d张图片'%(self.titlecount,self.pagecount,self.piccount))
tiebaname='人很少'
print(parse.urlencode({'kw':'%s'%(tiebaname)}))
spider=SpiderMain()
start=time.time()
spider.__crawl__(tiebaname)
end=time.time()
print(end-start)
下载网页
tieba_htmldownloader.py
# coding:utf8
import urllib.request
class HtmlDownloader(object):
def download(self,url):
if url is None:
return None
request=urllib.request.Request(url)
request.add_header("user-agent", "Mozilla/5.0")
response=urllib.request.urlopen(url)
if response.getcode() !=200:
return None
return response.read()
解析网页
tieba_parser.py
#coding:utf8
from bs4 import BeautifulSoup
import urllib.parse
import re
class HtmlParser(object):
def _get_page(self,page):
it=re.finditer(r"pn=d+", str(page))
# ithr=re.finditer(r"d+", str(out))
# print(out)
maxpage=0;
count=0;
for ma in it:
count+=1
maxpage=ma.group()
# print('maxage=',maxpage)
if count!=0:
maxpage=re.finditer(r"d+", maxpage).__next__().group()
# urlnum=ithr.__next__().group()
return int(maxpage)
return 0
def _get_outermaxpage(self,cont):
soup=BeautifulSoup(cont,'html.parser',from_encoding='utf-8')
lastpage=soup.find_all('a',href=re.compile(r'/f?.+'),class_='last')
print('lastpage=',lastpage)
return self._get_page(lastpage)
''' 传入 :outis ['<a href="/p/4078134144?pn=2">2</a><a href="/p/4078134144?pn=3">3</a><a href="/p/4078134144?pn=4">4</a><a href="/p/4078134144?pn=5">5</a><a href="/p/4078134144?pn=2">下一页</a><a href="/p/4078134144?pn=5">尾页</a>']
得到 :5 '''
def _get_innermaxpage(self,cont):
psoup=BeautifulSoup(cont,'html.parser',from_encoding='utf-8')
allpages=psoup.find_all('li',class_="l_pager pager_theme_4 pb_list_pager")
return self._get_page(allpages)
'''得到所有此页图片的链接'''
def _get_piclinks(self,cont):
#爬取每个标题内容
psoup=BeautifulSoup(cont,'html.parser',from_encoding='utf-8')
#找到图片
# '''<img class="BDE_Image" height="198" width="198" size="5002" src="http://imgsrc.baidu.com/forum/w%3D580/sign=44e7872b8d82b9013dadc33b438da97e/82d63af33a87e950be315f6017385343faf2b4ea.jpg" style="cursor: url("http://tb2.bdstatic.com/tb/static-pb/img/cur_zin.cur"), pointer;">'''
piclinks=psoup.find_all('img',src=re.compile(r"http://imgsrc.baidu.com.+"),class_="BDE_Image")
picurls=set()
#开始获取所有图片地址
for piclink in piclinks:
picurls.add(piclink['src'])
# print(piclink['src'])
return picurls
图片下载
tieba_picdownloader.py
#coding:utf8
'''
Created on 2016年1月26日
@author: thewindkee
'''
import os
import urllib.request
from tieba import tieba_htmldownloader
class PicDownloader(object):
def save(self,url,folder,savename,savetype):
#folder为要保存的文件夹,savename+savetype组成文件名.类型
#保存文件时候注意类型要匹配,如要保存的图片为jpg,则打开的文件的名称必须是jpg格式,否则会产生无效图片
path=os.getcwd()+'\%s'%(folder)
if not os.path.exists(path):
os.makedirs(folder)
pwd=''.join([path,'\',savename,savetype])
# pwd=''.join([os.getcwd(),'\',savename,savetype])
cont=tieba_htmldownloader.HtmlDownloader().download(url)
if cont is None:
print(cont)
return
file = open(pwd,'wb')
file.write(cont)
file.close()
print('Pic Saved!')
链接管理
url_manager.py
# coding:utf8
class UrlManager(object):
def __init__(self):
self.new_urls=set()
self.old_urls=set()
def add_new_url(self,url):
if url is None:
return
if url not in self.new_urls and url not in self.old_urls:
self.new_urls.add(url)
def add_new_urls(self,urls):
if urls is None or len(urls)==0:
return
for url in urls:
self.add_new_url(url)
def has_new_url(self):
return len(self.new_urls)!=0
def get_new_url(self):
new_url=self.new_urls.pop()
self.old_urls.add(new_url)
return new_url
抓出来的效果:
水平有限,请见谅。。写了第一篇博客,作为纪念。
PS:我最多爬了一千多个标题。而且其中有个贴吧,爬到图中的时候,突然在下载-保存图片的过程中停了下来,
但是控制台没有停,曾经以为是打开没有打开网页,导致后续动作停止,如果有知道的望告知。
2016-8-22 , 原因是没有设置timeout ,写成下面的就好了,超时会报异常。
with request.urlopen(url,timeout=10)as resp: