python单线程爬虫

python ldboyghg 35℃ 0评论

一、Requests介绍与安装

1 Requests介绍:HTTP for humans

Python的第三方HTTP库,可方便地实现Python的网络连接;

完美替代了Python的urllib2模块;

拥有更多自动化、更友好的用户体验、更完善的功能;

2 安装

在Windows下,执行pip install requests;

Linux下,执行sudo pip install requests;

3 第三方库安装技巧

3.1 少使用easy_install,因为只能安装不能卸载;

3.2 多使用pip方式安装;

3.3 撞墙怎么办?请戳http://www.lfd.uci.edu/~gohlke/pyhtonlibs/

4 举例

教学视频是根据上述网站下载requests库,但现在没法打开,因此在搜索引擎上搜索查找到requests下载链接:https://pypi.python.org/pypi/requests/

选择“requests-2.10.0-py2.py3-none-any.whl (md5)”进行下载;

修改下载文件的拓展名为.zip,并复制requests文件夹到Python安装目录下的Lib文件夹下即完成安装;

二、第一个网页爬虫

使用requests获取网页源代码,再使用正则表达式匹配出感兴趣的内容,这是单线程简单爬虫的基本原理;

1 使用requests获取网页源代码

1.1 直接获取源代码

  1. #-*-coding:utf8-*-
  2. import requests
  3. html=requests.get(‘http://www.nowcoder.com/courses’)
  4. print html.text

但有时通过上述Python程序是无法获取网页源代码,因为有的网页会有反爬虫机制;

1.2 修改HTTP头获取源代码;

  1. import re
  2. import requests
  3. # 相当于面具,让访问的网站误以为是浏览器再查看源代码
  4. # 获取User-Agent内容:在该网页右击选择审核元素,切换到Network栏,刷新页面,随便点击一项,在Headers一栏最下方就有相关信息
  5. # hea是字典,键是User-Agent,值就是后面一长串内容
  6. hea={‘User-Agent’:‘Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36′}
  7. # 在此处调用hea
  8. html=requests.get(‘http://jp.tingroom.com/yuedu/yd300p/’,headers=hea)
  9. # 将编码转为utf-8,否则中文会显示乱码
  10. html.encoding=‘utf-8′
  11. print html.text

2 requests配合正则表达式提取感兴趣的内容:打印该网页中的日文

先获取整个网页源代码;

再查看想要获取的内容的上下文有什么共同点,可以看到想要获取的内容都是以<span style=”color:#666666;”>开头,以</span></p></li>结尾,中间部分就是带获取内容(.*?);

  1. import re
  2. import requests
  3. hea={‘User-Agent’:‘Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36′}
  4. html=requests.get(‘http://jp.tingroom.com/yuedu/yd300p/’,headers=hea)
  5. html.encoding=‘utf-8′
  6. # print html.text
  7. course=re.findall(‘<span style=”color:#666666;”>(.*?)</span></p></li>’,html.text,re.S)
  8. for i in course:
  9.     print i

执行结果:

第二章 昔々、といってもせいぜい二十年ぐらい前のことなのだ…
挪威的森林(中日对照) 内容简介: 汉堡机场一曲忧郁的《挪…
藤野先生名文选读中日文对照 東京も格別のことはなかつた。上…
夏目漱石 我是猫(中日对照) 吾輩は猫である 夏目漱石 一 吾輩…
それお皿の絵柄よ 足のケガで中クラスの総合病院に入院しまし…
あるけちん坊(ぼう)な男がおりました。 毎日毎日,ご飯どき…
あるところに,たいへんへそまがりな息子(むすこ)がおりま…
向こうから,お医者(いしゃ)がやってきました。そこへ店(…
お盆休み(ぼんやすみ)に帰ってきた者(もの)同士(どうし…
昔(むかし),三太(さんた)という,ばかな息子がおりまし…
ある日のこと。 そこつ者が,瀬戸物(せともの)屋(や)へ,…
植木(うえき)の大好きな旦那(だんな)がおりました。 ある…
息子が表(おもて)で,凧(たこ)を揚(あ)げておりました…
ある夜(や)のこと,お寺(てら)の庭(にわ)で,小僧(こ…
はくうんしゅうしょく 一匹のトンボが夏の終わりを告げるわけ…
先日、ある研修で聞いた言葉ですが、 「学習」の本質を端的に…
闇夜(やみよ)に,二人の若い男が,こそこそ話しております…
ある役人が誕生日のときに、下役たちは彼が鼠年だと聞き、お…
東京の郊外に住む木村さんは、お酒を飲んでの失敗の多い人で…
ある人が新調した絹の裾/裙(すそ、はかま)を着用して外出…

三、向网页提交数据

1 Get与Post介绍

Get从服务器上获取数据,通过构造url中的参数实现功能;

Post向服务器传送数据,将数据放在header提交数据;

2 分析目标网站

目标网站https://www.crowdfunder.com/browse/deals

爬取内容:网页的所有公司名称

辅助工具:Chrome浏览器的审核元素中Network功能

核心方法:requests.post

核心步骤:构造表单 -> 提交表单 -> 获取返回信息

1‘ 打开网站,点击查看网页源代码,这是网页爬虫的必要习惯!

在源代码中,可先用公司名称搜索其在源代码中的位置,如公司名称MYFORCE在源代码位置如下:

公司名称可使用正则表达式”card-title”>(.*?)</div>进行匹配,此时可匹配到19个公司名称;

2‘ 在网站正下方点击SHOW MORE按钮,出现更多公司信息(以追加形式);

值得注意的是,此时网站网址仍是https://www.crowdfunder.com/browse/deals,说明该网站使用异步加载技巧

异步加载,指先加载网页基本框架,对于剩下的信息,需要哪里就加载哪里,提高网页加载效率

重新打开网站源代码,搜索”card-title”>,发现仍然是19,说明后面加载的公司信息在源代码中无法找到;

3′ 使用Chrome的审核元素中Network功能,当前没有任何信息;点击SHOW MORE按钮,出现信息,点击第一条:

在General项,请求方法是Request Method: POST,说明使用POST向网页提交数据;

提交地址是https://www.crowdfunder.com/browse/deals&template=false

在Form Data项,页数是Page: 2,说明构造Form Data向网页进行提交,可以得到想要的信息;

4‘ 打开Pycharm,

  1. #-*-coding:utf8-*-
  2. import requests
  3. import re
  4. url=‘https://www.crowdfunder.com/browse/deals&template=false’
  5. data={   # 字典,有2个元素,构造Form Data
  6.     ‘entities_only’:‘true’,
  7.     ‘page’:‘1’   # 可修改页数值
  8. }
  9. html_post=requests.post(url,data=data)
  10. title=re.findall(‘”card-title”>(.*?)</div>’,html_post.text,re.S)
  11. for each in title:
  12.     print each

结果执行:

MYCROWD QA
DIGITZS
GOCOIN
SANTO DIABLO MEZCAL
RAGEON!
SELFIE WITH ME

四、实战——极客学院课程爬虫

1 目标网站

http://www.jikexueyuan.com/course/

2 目标内容

前20页的课程名称、课程介绍、课程时间、课程等级、学习人数

3 涉及知识

requests获取网页、re.sub换页、正则表达式匹配内容

4 抓取关键步骤

1’ 打开网站,首页网站为http://www.jikexueyuan.com/course/;点击下一页后,第2页网站为http://www.jikexueyuan.com/course/?pageNum=2;

则首页网站也可以是http://www.jikexueyuan.com/course/?pageNum=1;

2‘ 首先点击审核元素中放大镜图标,将鼠标箭头放在第一个课程的名称处,审核元素就会自动定位到当前源代码相应位置,在其附近也可找到课程介绍、时间、等级和学习人数对应的源代码位置;

3’ 采用先抓大后抓小的原则。对于先抓大,在审核元素中,每个课程信息都是在标签<li id=”和标签</li>之间,可以使用re.findall()匹配出所有课程信息并保存在列表中;对于再抓小,对列表进行遍历,每个课程信息进行细划分,如下:

课程名称在标签class=”lessonimg” title=”和标签” alt=之间;

课程介绍在标签display: none;”>和标签</p>之间;

课程时间在标签<em>和标签</em>之间;

课程等级在标签<em>和标签</em>之间;

学习人数在标签”learn-number”>和标签</em>之间;

其中,对于相同的标签标识,可使用re.findall()匹配出2个符合要求的元素,课程时间在列表的第一个元素,课程等级在列表的第二个元素;

5 编写程序——从小到大功能实现

5.1 实现单页的课程信息爬取

  1. #-*-coding:utf8-*-
  2. import requests
  3. import re
  4. import sys
  5. reload(sys)
  6. sys.setdefaultencoding(“utf-8″)
  7. url=‘http://www.jikexueyuan.com/course/?pageNum=1′
  8. html=requests.get(url).text
  9. course=re.findall(‘(<li id=”.*?</li>)’,html,re.S)
  10. info={}
  11. for each in course:
  12.     info[‘title’]=re.search(‘class=”lessonimg” title=”(.*?)” alt=’,each,re.S).group(1)
  13.     info[‘detail’] = re.search(‘display: none;”>(.*?)</p>’, each, re.S).group(1)
  14.     temp=re.findall(‘<em>(.*?)</em>’,each,re.S)
  15.     info[‘date’]=temp[0]
  16.     info[‘class’]=temp[1]
  17.     info[‘number’]=re.search(‘”learn-number”>(.*?)</em>’,each,re.S).group(1)
  18.     f = open(‘info.txt’‘a’)
  19.     f.writelines(‘title: ‘+info[‘title’]+‘\n’)
  20.     f.writelines(‘detail: ‘ + info[‘detail’] + ‘\n’)
  21.     f.writelines(‘date: ‘ + info[‘date’] + ‘\n’)
  22.     f.writelines(‘class: ‘ + info[‘class’] + ‘\n’)
  23.     f.writelines(‘number: ‘ + info[‘number’] + ‘\n\n’)
  24.     f.close()

5.2 实现20页的课程信息爬取

  1. #-*-coding:utf8-*-
  2. import requests
  3. import re
  4. import sys
  5. reload(sys)
  6. sys.setdefaultencoding(“utf-8″)
  7. url=‘http://www.jikexueyuan.com/course/?pageNum=1′
  8. cur_url=int(re.search(‘pageNum=(\d+)’,url,re.S).group(1))
  9. page_group=[]
  10. for i in range(1,21):
  11.     links=re.sub(‘pageNum=\d+’,‘pageNum=%s’%i,url,re.S)
  12.     page_group.append(links)
  13. for link in page_group:
  14.     print u‘正在处理页面:’+link
  15.     html=requests.get(link).text
  16.     course=re.findall(‘(<li id=”.*?</li>)’,html,re.S)
  17.     info={}
  18.     for each in course:
  19.         info[‘title’]=re.search(‘class=”lessonimg” title=”(.*?) alt=’,each,re.S).group(1)
  20.         info[‘detail’] = re.search(‘display: none;”>(.*?)</p>’, each, re.S).group(1)
  21.         temp=re.findall(‘<em>(.*?)</em>’,each,re.S)
  22.         info[‘date’]=temp[0]
  23.         info[‘class’]=temp[1]
  24.         info[‘number’]=re.search(‘”learn-number”>(.*?)</em>’,each,re.S).group(1)
  25.         f = open(‘info.txt’‘a’)
  26.         f.writelines(‘title: ‘+info[‘title’]+‘\n’)
  27.         f.writelines(‘detail: ‘ + info[‘detail’] + ‘\n’)
  28.         f.writelines(‘date: ‘ + info[‘date’] + ‘\n’)
  29.         f.writelines(‘class: ‘ + info[‘class’] + ‘\n’)
  30.         f.writelines(‘number: ‘ + info[‘number’] + ‘\n\n’)
  31.         f.close()

5.3 实现使用类的课程信息爬取

  1. #-*_coding:utf8-*-
  2. import requests
  3. import re
  4. # Windows下默认编码是GBK,网页默认编码是utf-8,编码不匹配易导致爬取内容乱码
  5. import sys
  6. reload(sys)
  7. sys.setdefaultencoding(“utf-8″)
  8. class spider(object):
  9.     def __init__(self):
  10.         print u‘开始爬取内容。。。’
  11. #getsource用来获取网页源代码
  12.     def getsource(self,url):
  13.         html = requests.get(url)
  14.         return html.text
  15. #changepage用来生产不同页数的链接
  16.     def changepage(self,url,total_page):
  17.         now_page = int(re.search(‘pageNum=(\d+)’,url,re.S).group(1))
  18.         page_group = []
  19.         for i in range(now_page,total_page+1): # 左闭右开
  20.             link = re.sub(‘pageNum=\d+’,‘pageNum=%s’%i,url,re.S)
  21.             page_group.append(link)
  22.         return page_group
  23. #geteveryclass用来抓取每个课程块的信息
  24.     def geteveryclass(self,source):
  25.         everyclass = re.findall(‘(<li id=.*?</li>)’,source,re.S)
  26.         return everyclass
  27. #getinfo用来从每个课程块中提取出我们需要的信息
  28.     def getinfo(self,eachclass):
  29.         info = {} # 字典
  30.         info[‘title’] = re.search(‘lessonimg” title=”(.*?)” alt’,eachclass,re.S).group(1)
  31.         info[‘content’] = re.search(‘display: none;”>(.*?)</p>’,eachclass,re.S).group(1)
  32.         timeandlevel = re.findall(‘<em>(.*?)</em>’,eachclass,re.S)
  33.         info[‘classtime’] = timeandlevel[0]
  34.         info[‘classlevel’] = timeandlevel[1]
  35.         info[‘learnnum’] = re.search(‘”learn-number”>(.*?)</em>’,eachclass,re.S).group(1)
  36.         return info
  37. #saveinfo用来保存结果到info.txt文件中
  38.     def saveinfo(self,classinfo):
  39.         f = open(‘info.txt’,‘a’)
  40.         #追加方式打开
  41.         for each in classinfo:
  42.             f.writelines(‘title:’ + each[‘title’] + ‘\n’)
  43.             f.writelines(‘content:’ + each[‘content’] + ‘\n’)
  44.             f.writelines(‘classtime:’ + each[‘classtime’] + ‘\n’)
  45.             f.writelines(‘classlevel:’ + each[‘classlevel’] + ‘\n’)
  46.             f.writelines(‘learnnum:’ + each[‘learnnum’] +‘\n\n’)
  47.         f.close()
  48. #####程序入口
  49. #如果是程序自己使用jikexueyuan.py,则__name__的值是__main__
  50. if __name__ == ‘__main__’:
  51.     classinfo = [] # 空列表,用以保存课程信息
  52.     url = ‘http://www.jikexueyuan.com/course/?pageNum=1′
  53.     jikespider = spider()    # 实例化spider()类
  54.     all_links = jikespider.changepage(url,20)
  55.     for link in all_links:
  56.         print u‘正在处理页面:’ + link
  57.         html = jikespider.getsource(link)
  58.         everyclass = jikespider.geteveryclass(html)
  59.         for each in everyclass:
  60.             info = jikespider.getinfo(each)
  61.             classinfo.append(info)
  62.     jikespider.saveinfo(classinfo)

6 涉及知识点总结

6.1 修改默认编码

Windows默认编码是GBK,网页默认编码是UTF-8,需设置默认编码,否则易导致爬取内容乱码;

注意:在设置前必须对sys模块进行reload;

  1. import sys
  2. reload(sys)
  3. sys.setdefaultencoding(“utf-8″)

6.2 __name__内置属性

如果import一个模块,则模块的__name__的值为模块文件名,不带路径或文件扩展名;

如果直接执行当前文件,则__name__的值为__main__。

6.3 类的定义和实例化

6.4 re.search(pattern, string, flag)方法

pattern为匹配的正则表达式,string为要匹配的字符串,flag为标志位;

匹配整个字符,只提取第一个符合要求的内容,返回一个正则表达式对象;

使用匹配对象函数group(n)获取第n个匹配字符串,或group()获取整个字符串;

6.5 re.findall(pattern, string, flag)方法

匹配所有符合要求的内容,结果以列表形式返回;

可使用下标对列表元素进行访问;

6.6 re.sub(pattern, repl, string)方法

pattern为匹配的正则表达式,repl为替换的正则表达式,string为要匹配的字符串;

如re.sub(‘123(.*?)’,’123%d’%123,s)表示将123后面的字符串替换成123,这里的%d可以是任何一个字母;

6.7 正则表达式\d+

\d表示[0-9]

\l表示[a-z]

\u表示[A-Z]

\w表示[a-zA-Z0-9]

\s表示任意空白字符

上述大写形式表示其相反意义,如\D表示非数字字符

+表示有多个,如\d+表示至少有1个、最多不限制的数字串

^表示以其后紧跟的字符类型开头,如^[0-9]表示以数字字符为开头,注意:[^0-9]表示非数字字符

$表示以其前紧跟的字符类型结尾

6.8 文件I/O

用file对象实现文件操作;

a 打开:open()方法

file_object=open(file_name[, access_mode]);

file_name表示要访问的文件名的字符串值,access_mode表示打开文件模式:只读r、写入w、追加a等;

b 关闭:close()方法

刷新缓存区重任何还没写入的信息,并关闭该文件;

file_object.close();

c 写:

write()方法:

file_object.write(string);

将任何字符串写入一个打开的文件;

writelines()方法:

file_object.writelines(sequence);

将序列以迭代形式写入一个打开的文件;

d 读:

read()方法:

string=file_object.read([len]);

从打开的文件中读取一个字符串,默认读到文件末尾,否则读取前len个字符,以字符串形式返回;

readline()方法:

string=file_object.readline();

从打开的文件中读取整行,包括换行符,以字符串形式返回;

sequence=file_object.readlines();

从打开的文件中读取所有行,以列表形式返回;

—————————————————————

课程总结:

1 requests获取网页源代码

2 修改http头绕过简单的反爬虫机制

3 向网页提交内容

转载请注明:生命不息,奋斗不止 » python单线程爬虫

喜欢 (0)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址