【极客学院】-python学习笔记-4-单线程爬虫 (提交表单抓取信息,实战练习)
2016-01-12 15:55
986 查看
极客学院课程地址:http://www.jikexueyuan.com/course/821_3.html?ss=1
向网页提交数据:
get,post 交互方式
分析目标网站
表单提交功能
get:从服务器获得数据
post:向服务器传数据
eg:
https://www.crowdfunder.com/browse/deals
request.post
步骤:构造表单,提交,获取返回信息
异步加载:菜做好一道上一道
不同于 菜做好了,再一次性上齐
网页 → 审查 → Headers → Request Method:GET (页面并没有 more,这里也没有POST)
Form Data → page:2
当有多个 page 的时候,直接提取,可能因为异步加载,无法提取到 more 的信息
这时 可以建立一个字典,并用 request.post 去提交表单,提取数据
继续
实战:
http://www.jikexueyuan.com/course/
前20页的 课程名称,课程介绍,课程时间,课程等级,学习人数
步骤:
requests获取网页
re.sub换页
正则表达式匹配内容
问题:
课程介绍,后面是省略号 的情况该怎么办?
爬虫只能爬到可以看到的
有的课程的课程介绍也可能为空
解决方案:
先抓大后抓小:
另:windows下将命令提示符的编码强制转化成utf-8
实践中遇到问题:
按课程中的代码敲出来后,会有一些错误
比如运行后,info.txt 中没有内容:
因为:网页的元素不是课程里的那样了,所以需要修改匹配的线索,这样修改了 everyclass,title 和 content,
<li id="2430" test="0" deg="0" >
但是到 timeandlevel 又有错误了
说明:
“XXX”object is not subscriptable
说的是XXX对象不是可索引的,可索引的对象有list,tuple等
如果你确定你的XXX是一个可迭代对象的话,可以尝试用list()函数把它转化为列表,然后通过索引读取元素
尝试分部找规律,提出 time 和 level,
但是 有的 level 的 icon 后面可能会加数字,导致,查找出的结果会有none
<i class="xinhao-icon2"></i><em>中级</em>
后来发现问题在于,把 re.findall 写成了 re.search
所以,只查找了一个 em,没办法分配哦
而实际上是这样的数据
最终代码:
知识点:
参考:
Python中re(正则表达式)模块学习 http://www.cnblogs.com/sevenyuan/archive/2010/12/06/1898075.html
[1]re.search
re.search函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回,如果字符串没有匹配,则返回None。
re.search的函数原型为: re.search(pattern, string, flags)
每个参数的含意与re.match一样。
re.match与re.search的区别:
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
python group()
参考:http://www.cnblogs.com/kaituorensheng/archive/2012/08/20/2648209.html
正则表达式中,group()用来提出分组截获的字符串,()用来分组
例:
re.match
re.match 尝试从字符串的开始匹配一个模式,如:下面的例子匹配第一个单词。
re.match的函数原型为:re.match(pattern, string, flags)
第一个参数是正则表达式,这里为"(\w+)\s",如果匹配成功,则返回一个Match,否则返回一个None;
第二个参数表示要匹配的字符串;
第三个参数是标致位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
[2]re.sub
re.sub用于替换字符串中的匹配项。下面一个例子将字符串中的空格 ' ' 替换成 '-' :
re.sub的函数原型为:re.sub(pattern, repl, string, count)
其中第二个参数是替换后的字符串;本例中为'-'
第四个参数指替换个数。默认为0,表示每个匹配项都替换。
re.sub还允许使用函数对匹配项的替换进行复杂的处理。如:re.sub(r'\s', lambda m: '[' + m.group(0) + ']', text, 0);将字符串中的空格' '替换为'[ ]'。
向网页提交数据:
get,post 交互方式
分析目标网站
表单提交功能
get:从服务器获得数据
post:向服务器传数据
eg:
https://www.crowdfunder.com/browse/deals
request.post
步骤:构造表单,提交,获取返回信息
异步加载:菜做好一道上一道
不同于 菜做好了,再一次性上齐
网页 → 审查 → Headers → Request Method:GET (页面并没有 more,这里也没有POST)
Form Data → page:2
当有多个 page 的时候,直接提取,可能因为异步加载,无法提取到 more 的信息
这时 可以建立一个字典,并用 request.post 去提交表单,提取数据
#-*-coding:utf8-*- import requests import re url='https://www.crowdfunder.com/' data = { 'entities_only':'true'<span style="background-color: rgb(153, 255, 153);">,</span> 'page':'1' } html_post = requests.post(url<span style="background-color: rgb(153, 255, 153);">,</span>data=data) title = re.findall('"card-title">(.*?)</div>', html_post.text, re.S) for each in title: print (each)
继续
实战:
http://www.jikexueyuan.com/course/
前20页的 课程名称,课程介绍,课程时间,课程等级,学习人数
步骤:
requests获取网页
re.sub换页
正则表达式匹配内容
问题:
课程介绍,后面是省略号 的情况该怎么办?
爬虫只能爬到可以看到的
有的课程的课程介绍也可能为空
解决方案:
先抓大后抓小:
另:windows下将命令提示符的编码强制转化成utf-8
import sys reload(sys) sys.setdefaultencoding("utf-8")
实践中遇到问题:
按课程中的代码敲出来后,会有一些错误
比如运行后,info.txt 中没有内容:
因为:网页的元素不是课程里的那样了,所以需要修改匹配的线索,这样修改了 everyclass,title 和 content,
<li id="2430" test="0" deg="0" >
但是到 timeandlevel 又有错误了
TypeError: '_sre.SRE_Match' object is not subscriptable
说明:
“XXX”object is not subscriptable
说的是XXX对象不是可索引的,可索引的对象有list,tuple等
如果你确定你的XXX是一个可迭代对象的话,可以尝试用list()函数把它转化为列表,然后通过索引读取元素
尝试分部找规律,提出 time 和 level,
info['classtime'] = re.search('"time-icon"></i><em>(.*?)</em>',eachclass,re.S).group(1) info['classlevel'] = re.search('"xinhao-<span style="color:#ff0000;">icon</span>"></i><em>(.*?)</em>',eachclass,re.S).group(1)
但是 有的 level 的 icon 后面可能会加数字,导致,查找出的结果会有none
<i class="xinhao-icon2"></i><em>中级</em>
后来发现问题在于,把 re.findall 写成了 re.search
timeandlevel = re.search('<em>(.*?)</em>',eachclass,re.S)
所以,只查找了一个 em,没办法分配哦
<_sre.SRE_Match object; span=(1122, 1146), match='<em>3课时\n\t\t\t\t\t\t\t57分钟</em>'>
而实际上是这样的数据
<dd class="mar-b8"><i class="time-icon"></i><em>4课时 <span style="white-space:pre"> </span>56分钟</em> </dd> <dd class="zhongji"> <span style="white-space:pre"> </span><i class="xinhao-icon"></i><em>初级</em> </dd>
最终代码:
#-*-coding:utf8-*- import requests import re #url = 'http://www.jikexueyuan.com/course/' #html = requests.get(url) #print(html.text) #content = re.findall('', html.text, re.S) class spider(object): def __init__(self): print (u'开始爬取内容...') def changepage(self,url,total_page): page_group = [] #获得需要爬取的所有 page 的列表 now_page = int(re.search('pageNum=(\d+)', url, re.S).group(1)) for i in range(now_page,total_page+1): link = re.sub('pageNum=\d+','pageNum=%s'%i,url, re.S) page_group.append(link) return page_group def getsource(self,url): html = requests.get(url) #获取网页源代码 return html.text def geteveryclass(self,source): #everyclass = re.findall('(<li deg="".*?</li>)',source,re.S ) #先把所有课程找出来 everyclass = re.findall('(<li id=.*?</li>)',source,re.S ) return everyclass def getinfo(self,eachclass): info = {} #定义字典 # info['title'] = re.search('target="_blank">(.*?)</a>',eachclass,re.S).group(1) info['title'] = re.search('title="(.*?)" alt',eachclass,re.S).group(1) # info['content'] = re.search('</h2><p>(.*?)</p>',eachclass,re.S).group(1) info['content'] = re.search('none;">(.*?)</p>',eachclass,re.S).group(1) #timeandlevel = re.search('<em>(.*?)</em>',eachclass,re.S) timeandlevel = re.<span style="background-color: rgb(153, 255, 153);">findall</span>('<em>(.*?)</em>',eachclass,re.S) info['classtime'] = timeandlevel[0] info['classlevel'] = timeandlevel[1] # info['classtime'] = re.search('"time-icon"></i><em>(.*?)</em>',eachclass,re.S).group(1) # info['classlevel'] = re.search('"xinhao-icon"></i><em>(.*?)</em>',eachclass,re.S).group(1) info['learnnum'] = re.search('"learn-number">(.*?)</em>',eachclass,re.S).group(1) return info def saveinfo(self,classinfo): f = open('info.txt','a') #打开 info.txt, 使用追加的方式 for each in classinfo: f.writelines('title:' + each['title'] + '\n') f.writelines('content:' + each['content'] + '\n') f.writelines('classtime:' + each['classtime'] + '\n') f.writelines('classlevel:' + each['classlevel'] + '\n') f.writelines('learnnum:' + each['learnnum'] + '\n\n') f.close() #程序入口 if __name__ == '__main__': #如果程序是自己使用 .py 文件运行 classinfo = [] #定义一个列表用来存课程信息 url = 'http://www.jikexueyuan.com/course/?pageNum=1' jikespider = spider() #实例化 类 all_links = jikespider.changepage(url,2) for link in all_links: print(u'正在处理页面: '+ link) html = jikespider.getsource(link) everyclass = jikespider.geteveryclass(html) for each in everyclass: info = jikespider.getinfo(each) classinfo.append(info) jikespider.saveinfo(classinfo)
知识点:
参考:
Python中re(正则表达式)模块学习 http://www.cnblogs.com/sevenyuan/archive/2010/12/06/1898075.html
[1]re.search
re.search函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回,如果字符串没有匹配,则返回None。
re.search的函数原型为: re.search(pattern, string, flags)
每个参数的含意与re.match一样。
re.match与re.search的区别:
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
now_page = int(re.search('pageNum=(\d+)', url, re.S).group(1))
python group()
参考:http://www.cnblogs.com/kaituorensheng/archive/2012/08/20/2648209.html
正则表达式中,group()用来提出分组截获的字符串,()用来分组
例:
import re a = "123abc456" print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0) #123abc456,返回整体 print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1) #123 print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2) #abc print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3) #456
re.search('pageNum=(\d+)', url, re.S) <span style="white-space:pre"> </span>#<_sre.SRE_Match object; span=(35, 44), match='pageNum=1'>
re.search('pageNum=(\d+)', url, re.S).group(1)<span style="white-space:pre"> </span># 1
re.match
re.match 尝试从字符串的开始匹配一个模式,如:下面的例子匹配第一个单词。
re.match的函数原型为:re.match(pattern, string, flags)
第一个参数是正则表达式,这里为"(\w+)\s",如果匹配成功,则返回一个Match,否则返回一个None;
第二个参数表示要匹配的字符串;
第三个参数是标致位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
[2]re.sub
re.sub用于替换字符串中的匹配项。下面一个例子将字符串中的空格 ' ' 替换成 '-' :
re.sub的函数原型为:re.sub(pattern, repl, string, count)
其中第二个参数是替换后的字符串;本例中为'-'
第四个参数指替换个数。默认为0,表示每个匹配项都替换。
re.sub还允许使用函数对匹配项的替换进行复杂的处理。如:re.sub(r'\s', lambda m: '[' + m.group(0) + ']', text, 0);将字符串中的空格' '替换为'[ ]'。
for i in range(1,3): link = re.sub('pageNum=\d+','pageNum=%s'%i,url, re.S) print(link)
结果:
http://www.jikexueyuan.com/course/?pageNum=1 http://www.jikexueyuan.com/course/?pageNum=2[/code]
[3]re.findall
re.findall可以获取字符串中所有匹配的字符串。如:re.findall(r'\w*oo\w*', text);获取字符串中,包含'oo'的所有单词。everyclass = re.findall('(<li id=.*?</li>)',source,re.S)timeandlevel = re.findall('<em>(.*?)</em>',eachclass,re.S) info['classtime'] = timeandlevel[0] info['classlevel'] = timeandlevel[1]
相关文章推荐
- [Leetcode]@python 66. Plus One
- 笨办法学Python学习笔记 练习16
- twistd 启动Python服务
- Python -- dict 类
- Python标准库
- python常用随机函数及带权重随机实例
- Python 2.5.7 高阶函数
- python笔记-列表和元组
- 《Web Scraping with Python》读书笔记
- python 2.5.6 函数式编程
- python笔记-字符串
- python数字图像处理(8):对比度与亮度调整
- Python的 startswith 和endswith
- python 2.5.5 迭代器
- Python学习手册 - 09
- python文件操作
- python的 __name__和__doc__属性
- 自学Python二 Python中的屠龙刀(续)
- VirtualEnv安装
- python数字图像处理(7):图像的形变与缩放