版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
PythonCrawlerDevelopment極客學(xué)院J互聯(lián)網(wǎng)+職業(yè)技能系列
Python爬蟲開發(fā)從入門到實(shí)戰(zhàn)(微課版)人民郵電出版社謝乾坤
著
PythonCrawlerDevelopmen1第3章正則表達(dá)式與文件操作
在爬蟲的開發(fā)中,需要把有用的信息從一大段文本中提取出來(lái)。正則表達(dá)式是提取信息的方法之一。
正則表達(dá)式雖然不是最簡(jiǎn)單的也不是最高效的數(shù)據(jù)提取方法,但它是最直接的。而且在某些情況下,只有使用正則表達(dá)式才能達(dá)到目的。學(xué)好正則表達(dá)式,是開發(fā)爬蟲的第一步。第3章正則表達(dá)式與文件操作在爬蟲的開發(fā)2
通過(guò)這一章的學(xué)習(xí),你將會(huì)掌握如下知識(shí)。(1)正則表達(dá)式的基本符號(hào)。(2)如何在Python中使用正則表達(dá)式。(3)正則表達(dá)式的提取技巧。(4)Python讀寫文本文件和CSV文件。通過(guò)這一章的學(xué)習(xí),你將會(huì)掌握如下知識(shí)33.1正則表達(dá)式
正則表達(dá)式(RegularExpression)是一段字符串,它可以表示一段有規(guī)律的信息。Python自帶一個(gè)正則表達(dá)式模塊,通過(guò)這個(gè)模塊可以查找、提取、替換一段有規(guī)律的信息。
在程序開發(fā)中,要讓計(jì)算機(jī)程序從一大段文本中找到需要的內(nèi)容,就可以使用正則表達(dá)式來(lái)實(shí)現(xiàn)。
使用正則表達(dá)式有如下步驟。(1)尋找規(guī)律。(2)使用正則符號(hào)表示規(guī)律。(3)提取信息。3.1正則表達(dá)式正則表達(dá)式(Regul41.點(diǎn)號(hào)“.”
一個(gè)點(diǎn)號(hào)可以代替除了換行符以外的任何一個(gè)字符,包括但不限于英文字母、數(shù)字、漢字、英文標(biāo)點(diǎn)符號(hào)和中文標(biāo)點(diǎn)符號(hào)。2.星號(hào)“*”
一個(gè)星號(hào)可以表示它前面的一個(gè)子表達(dá)式(普通字符、另一個(gè)或幾個(gè)正則表達(dá)式符號(hào))0次到無(wú)限次。3.1.1正則表達(dá)式的基本符號(hào)1.點(diǎn)號(hào)“.”3.1.1正則表達(dá)式的基本符號(hào)53.問(wèn)號(hào)“?”
問(wèn)號(hào)表示它前面的子表達(dá)式0次或者1次。注意,這里的問(wèn)號(hào)是英文問(wèn)號(hào)。
4.反斜杠“\”
反斜杠在正則表達(dá)式里面不能單獨(dú)使用,甚至在整個(gè)Python里都不能單獨(dú)使用。反斜杠需要和其他的字符配合使用來(lái)把特殊符號(hào)變成普通符號(hào),把普通符號(hào)變成特殊符號(hào)。3.問(wèn)號(hào)“?”6
反斜杠不僅可以把特殊符號(hào)變成普通符號(hào),還可以把普通符號(hào)變成特殊符號(hào)。
例如“n”只是一個(gè)普通的字母,但是“\n”代表?yè)Q行符。
在Python開發(fā)中,經(jīng)常遇到的轉(zhuǎn)義字符,如表3-1所示。反斜杠不僅可以把特殊符號(hào)變成普通符號(hào),還可7轉(zhuǎn)義字符意義\n換行符\t制表符\\普通的反斜杠\'單引號(hào)\"雙引號(hào)\d數(shù)字
表3-1常見(jiàn)的轉(zhuǎn)義字符轉(zhuǎn)義字符意義\n換行符\t制表符\\普通的反斜杠\'單引號(hào)\85.?dāng)?shù)字“\d”
正則表達(dá)式里面使用“\d”來(lái)表示一位數(shù)字。為什么要用字母d呢?因?yàn)閐是英文“digital(數(shù)字)”的首字母。
再次強(qiáng)調(diào)一下,“\d”雖然是由反斜杠和字母d構(gòu)成的,但是要把“\d”看成一個(gè)正則表達(dá)式符號(hào)整體。
6.小括號(hào)“()”
小括號(hào)可以把括號(hào)里面的內(nèi)容提取出來(lái)。5.?dāng)?shù)字“\d”93.1.2在Python中使用正則表達(dá)式Python已經(jīng)自帶了一個(gè)功能非常強(qiáng)大的正則表達(dá)式模塊。使用這個(gè)模塊可以非常方便地通過(guò)正則表達(dá)式來(lái)從一大段文字中提取有規(guī)律的信息。Python的正則表達(dá)式模塊名字為“re”,也就是“regularexpression”的首字母縮寫。在Python中需要首先導(dǎo)入這個(gè)模塊再進(jìn)行使用。導(dǎo)入的語(yǔ)句為:importre3.1.2在Python中使用正則表達(dá)式Pyt101.findallPython的正則表達(dá)式模塊包含一個(gè)findall方法,它能夠以列表的形式返回所有滿足要求的字符串。
findall的函數(shù)原型為:re.findall(pattern,string,flags=0)pattern表示正則表達(dá)式,string表示原來(lái)的字符串,flags表示一些特殊功能的標(biāo)志。
1.findall11findall的結(jié)果是一個(gè)列表,包含了所有的匹配到的結(jié)果。如果沒(méi)有匹配到結(jié)果,就會(huì)返回空列表,如圖3-1所示。圖3-1findall返回的內(nèi)容findall的結(jié)果是一個(gè)列表,包含了所有12
當(dāng)需要提取某些內(nèi)容的時(shí)候,使用小括號(hào)將這些內(nèi)容括起來(lái),這樣才不會(huì)得到不相干的信息。如果包含多個(gè)“(.*?)”怎么返回呢?如圖3-2所示,返回的仍然是一個(gè)列表,但是列表里面的元素變?yōu)榱嗽M,元組里面的第1個(gè)元素是賬號(hào),第2個(gè)元素為密碼。圖3-2多個(gè)括號(hào)內(nèi)的內(nèi)容會(huì)以元組形式返回當(dāng)需要提取某些內(nèi)容的時(shí)候,使用小括號(hào)將這些13
請(qǐng)注意代碼中的冒號(hào)和逗號(hào),圖3-1代碼中為中文冒號(hào)和中文逗號(hào);圖3-2代碼中為英文冒號(hào)和英文逗號(hào)。在實(shí)際使用正則表達(dá)式的過(guò)程中,中英文標(biāo)點(diǎn)符號(hào)混淆常常會(huì)導(dǎo)致各種問(wèn)題。特別是冒號(hào)、逗號(hào)和引號(hào),雖然中英文看起來(lái)非常相似,但實(shí)際上中文冒號(hào)和英文冒號(hào)是不一樣的,中文逗號(hào)和英文逗號(hào)也是不一樣的。
在某些字體里面,這種差異甚至無(wú)法察覺(jué),因此在涉及正則表達(dá)式中的標(biāo)點(diǎn)符號(hào)時(shí),最好直接復(fù)制粘貼,而不要手動(dòng)輸入。請(qǐng)注意代碼中的冒號(hào)和逗號(hào),圖3-1代碼中為中14
函數(shù)原型中有一個(gè)flags參數(shù)。這個(gè)參數(shù)是可以省略的。當(dāng)不省略的時(shí)候,具有一些輔助功能,例如忽略大小寫、忽略換行符等。這里以忽略換行符為例來(lái)進(jìn)行說(shuō)明,如圖3-3所示。圖3-3使用re.S作為flag來(lái)忽略換行符函數(shù)原型中有一個(gè)flags參數(shù)。這個(gè)參數(shù)是15
在爬蟲的開發(fā)過(guò)程中非常容易出現(xiàn)這樣的情況,要匹配的內(nèi)容存在換行符“\n”。要忽略換行符,就需要使用到“re.S”這個(gè)flag。雖然說(shuō)匹配到的結(jié)果中出現(xiàn)了“\n”這個(gè)符號(hào),不過(guò)總比什么都得不到強(qiáng)。內(nèi)容里面的換行符在后期清洗數(shù)據(jù)的時(shí)候把它替換掉即可。在爬蟲的開發(fā)過(guò)程中非常容易出現(xiàn)這樣的情況,162.searchsearch()的用法和findall()的用法一樣,但是search()只會(huì)返回第1個(gè)滿足要求的字符串。一旦找到符合要求的內(nèi)容,它就會(huì)停止查找。對(duì)于從超級(jí)大的文本里面只找第1個(gè)數(shù)據(jù)特別有用,可以大大提高程序的運(yùn)行效率。
search()的函數(shù)原型為:re.search(pattern,string,flags=0)2.search17
對(duì)于結(jié)果,如果匹配成功,則是一個(gè)正則表達(dá)式的對(duì)象;如果沒(méi)有匹配到任何數(shù)據(jù),就是None。
如果需要得到匹配到的結(jié)果,則需要通過(guò).group()這個(gè)方法來(lái)獲取里面的值,如圖3-4所示。圖3-4使用.group()來(lái)獲取search()方法找到的結(jié)果對(duì)于結(jié)果,如果匹配成功,則是一個(gè)正則表達(dá)式18
只有在.group()里面的參數(shù)為1的時(shí)候,才會(huì)把正則表達(dá)式里面的括號(hào)中的結(jié)果打印出來(lái)。.group()的參數(shù)最大不能超過(guò)正則表達(dá)式里面括號(hào)的個(gè)數(shù)。參數(shù)為1表示讀取第1個(gè)括號(hào)中的內(nèi)容,參數(shù)為2表示讀取第2個(gè)括號(hào)中的內(nèi)容,以此類推,如圖3-5所示。
只有在.group()里面的參數(shù)為1的時(shí)候,19圖3-5.group()的參數(shù)意義圖3-5.group()的參數(shù)意義203.“.*”和“.*?”的區(qū)別
在爬蟲開發(fā)中,.*?這3個(gè)符號(hào)大多數(shù)情況下一起使用。
點(diǎn)號(hào)表示任意非換行符的字符,星號(hào)表示匹配它前面的字符0次或者任意多次。所以“.*”表示匹配一串任意長(zhǎng)度的字符串任意次。這個(gè)時(shí)候必須在“.*”的前后加其他的符號(hào)來(lái)限定范圍,否則得到的結(jié)果就是原來(lái)的整個(gè)字符串。
如果在“.*”的后面加一個(gè)問(wèn)號(hào),變成“.*?”,那么可以得到什么樣的結(jié)果呢?問(wèn)號(hào)表示匹配它前面的符號(hào)0次或者1次。于是.*?的意思就是匹配一個(gè)能滿足要求的最短字符串。
3.“.*”和“.*?”的區(qū)別21
這樣說(shuō)起來(lái)還是非常抽象,下面通過(guò)一個(gè)實(shí)際的例子來(lái)進(jìn)行說(shuō)明。請(qǐng)看下面這一段話:我的微博密碼是:1234567,QQ密碼是:33445566,銀行卡密碼是:888888,Github密碼是:999abc999,幫我記住它們
這段話有一個(gè)顯著的規(guī)律,即密碼是:xxxxxx,”,也就是在“密碼是”這3個(gè)漢字的后面跟一個(gè)中文的冒號(hào),冒號(hào)后面是密碼,密碼后面是中文的逗號(hào)。
這樣說(shuō)起來(lái)還是非常抽象,下面通過(guò)一個(gè)實(shí)際的例22
如果想把這4個(gè)密碼提取出來(lái),可以構(gòu)造以下兩個(gè)正則表達(dá)式:密碼是:(.*),密碼是:(.*?),
配合Python的findall方法,得到結(jié)果如圖3-6圖所示。
如果想把這4個(gè)密碼提取出來(lái),可以構(gòu)造以下23圖3-6使用“.*”和“.*?”返回的結(jié)果圖3-6使用“.*”和“.*?”返回的結(jié)果24
使用“(.*)”得到的是只有一個(gè)元素的列表,里面是一個(gè)很長(zhǎng)的字符串。
使用第2個(gè)正則表達(dá)式“(.*?)”,得到的結(jié)果是包含4個(gè)元素的列表,每個(gè)元素直接對(duì)應(yīng)原來(lái)文本中的每個(gè)密碼。
一句話總結(jié)如下。①“.*”:貪婪模式,獲取最長(zhǎng)的滿足條件的字符串。②“.*?”:非貪婪模式,獲取最短的能滿足條件的字符串。使用“(.*)”得到的是只有一個(gè)元素的列表253.1.3正則表達(dá)式提取技巧
1.不需要compile網(wǎng)上很多人的文章中,正則表達(dá)式使用pile()這個(gè)方法,導(dǎo)致代碼變成下面這樣:importreexample_text='我是kingname,我的微博賬號(hào)是:kingname,密碼是:12345678,QQ賬號(hào)是:99999,密碼是:890abcd,銀行卡賬號(hào)是:000001,密碼是:654321,Github賬號(hào)是:99999@,密碼是:7777love8888,請(qǐng)記住他們。'new_pattern=pile('賬號(hào)是:(.*?),密碼是:(.*?),',re.S)user_pass=re.findall(new_pattern,example_text)print(user_pass)3.1.3正則表達(dá)式提取技巧
1.不需要compile26這種寫法雖然結(jié)果正確,但純粹是畫蛇添足,是對(duì)Python的正則表達(dá)式模塊沒(méi)有理解透徹的體現(xiàn),是從其他啰嗦的編程語(yǔ)言中帶來(lái)的壞習(xí)慣。如果閱讀Python的正則表達(dá)式模塊的源代碼,就可以看出pile()是完全沒(méi)有必要的。對(duì)比pile()和re.findall()在源代碼中的寫法,如圖3-7所示的兩個(gè)方框。這種寫法雖然結(jié)果正確,但純粹是畫蛇添足,是對(duì)Pyt27圖3-7Python正則表達(dá)式模塊中的re.findall()和pile()圖3-7Python正則表達(dá)式模塊中的re.findal28
使用pile()的時(shí)候,程序內(nèi)部調(diào)用的是_compile()方法;當(dāng)使用re.finall()的時(shí)候,在模塊內(nèi)部自動(dòng)先調(diào)用了_compile()方法,再調(diào)用findall()方法。re.findall()自帶pile()的功能,所以沒(méi)有必要使用pile()。Python3中正則表達(dá)式模塊的源代碼的入口文件為re.py。這個(gè)文件里面的注釋就是學(xué)習(xí)Python正則表達(dá)式模塊非常好的文檔,它包含了正則表達(dá)式各種符號(hào)的簡(jiǎn)單說(shuō)明和這個(gè)模塊內(nèi)部各個(gè)方法的使用,如圖3-8所示。
使用pile()的時(shí)候,程序內(nèi)部29圖3-8Pythonre.py文件自帶的文檔圖3-8Pythonre.py文件自帶的文檔302.先抓大再抓小
一些無(wú)效內(nèi)容和有效內(nèi)容可能具有相同的規(guī)則。這種情況下很容易把有效內(nèi)容和無(wú)效內(nèi)容混在一起,如下面這段文字:
有效用戶:姓名:張三姓名:李四姓名:王五2.先抓大再抓小31無(wú)效用戶:姓名:不知名的小蝦米姓名:隱身的張大俠
有效用戶和無(wú)效用戶的名字前面都以“姓名:”開頭,如果使用“姓名:(.*?)\n”來(lái)進(jìn)行匹配,就會(huì)把有效信息和無(wú)效信息混在一起,難以區(qū)分,如圖3-9所示。無(wú)效用戶:32圖3-9使用“姓名:(.*?)\n”導(dǎo)致有效內(nèi)容和無(wú)效內(nèi)容混在一起圖3-9使用“姓名:(.*?)\n”導(dǎo)致有效內(nèi)容和無(wú)效33
要解決這個(gè)問(wèn)題,就需要使用先抓大再抓小的技巧。先把有效用戶這個(gè)整體匹配出來(lái),再?gòu)挠行в脩衾锩嫫ヅ涑鋈嗣?,代碼和運(yùn)行效果如圖3-10所示。先抓大再抓小的思想會(huì)貫穿整個(gè)爬蟲開發(fā)過(guò)程,一定要重點(diǎn)掌握。圖3-10代碼和運(yùn)行效果要解決這個(gè)問(wèn)題,就需要使用先抓大再抓小的技巧343.括號(hào)內(nèi)和括號(hào)外
在上面的例子中,括號(hào)和“.*?”都是一起使用的,因此可能會(huì)有讀者認(rèn)為括號(hào)內(nèi)只能有這3種字符,不能有其他普通的字符。但實(shí)際上,括號(hào)內(nèi)也可以有其他字符,對(duì)匹配結(jié)果的影響如圖3-11所示。圖3-11括號(hào)里有無(wú)其他字符對(duì)匹配結(jié)果的影響3.括號(hào)內(nèi)和括號(hào)外圖3-11括號(hào)里有無(wú)其他字符對(duì)匹配結(jié)果353.2Python文件操作Python的文件操作涉及對(duì)文件的讀/寫與編碼的處理,是學(xué)習(xí)爬蟲的必備知識(shí)。3.2.1使用Python讀/寫文本文件使用Python來(lái)讀/寫文本需要用到“open”這個(gè)關(guān)鍵字。它的作用是打開一個(gè)文件,并創(chuàng)建一個(gè)文件對(duì)象。使用Python打開文件,有兩種寫法。第1種方式如下:f=open('文件路徑','文件操作方式',encoding='utf-8')對(duì)文件進(jìn)行操作f.close()3.2Python文件操作Python的文件操36第2種方式,使用Python的上下文管理器:withopen('文件路徑','文件操作方式',encoding='utf-8')asf:
對(duì)文件進(jìn)行操作第1種方式需要手動(dòng)關(guān)閉文件,但是在程序開發(fā)中經(jīng)常會(huì)出現(xiàn)忘記關(guān)閉文件的情況。第2種方法不需要手動(dòng)關(guān)閉文件,只要代碼退出了縮進(jìn),Python就會(huì)自動(dòng)關(guān)閉文件。第2種方式,使用Python的上下文管理器:371.使用Python讀文本文件
使用Python打開一個(gè)文本文件時(shí),首先要保證這個(gè)文件是存在的。在讀文件的時(shí)候,“文件操作方式”這個(gè)參數(shù)可以省略,也可以寫成“r”,也就是read的首字母。
文件路徑可以是絕對(duì)路徑,也可以是相對(duì)路徑。如果是絕對(duì)路徑,Linux和MacOS不能直接使用“~”表示“home目錄”,因?yàn)镻ython不認(rèn)識(shí)“~”這個(gè)符號(hào)。如果非要使用這個(gè)符號(hào),需要使用Python的“os”模塊,代碼如下:importosreal_path=os.path.expanduser('~/project/xxx')1.使用Python讀文本文件38
這樣,Python就會(huì)將這種風(fēng)格的路徑轉(zhuǎn)化為Python能認(rèn)識(shí)的絕對(duì)路徑。
相對(duì)路徑是文本文件相對(duì)于現(xiàn)在的工作區(qū)而言的路徑,并不總是相對(duì)于當(dāng)前正在運(yùn)行的這個(gè)Python文件的路徑。在本章中,請(qǐng)讀者直接將文本文件和Python文件放在一起,這樣就可以直接使用文件名來(lái)打開文本文件。
文本文件的內(nèi)容和它相對(duì)于.py文件的位置如圖3-12所示。這樣,Python就會(huì)將這種風(fēng)格的路徑轉(zhuǎn)化為39圖3-12文本文件的內(nèi)容和它相對(duì)于.py文件的位置圖3-12文本文件的內(nèi)容和它相對(duì)于.py文件的位置40
使用下面的代碼來(lái)打開text.txt文件:withopen('text.txt',encoding='utf-8')asf:通過(guò)f來(lái)讀文件
這里有一個(gè)參數(shù)“encoding”。這個(gè)參數(shù)特別有用,它可以在打開文件的時(shí)候?qū)⑽募D(zhuǎn)換為UTF-8編碼格式,從而避免亂碼的出現(xiàn)。這個(gè)參數(shù)只有Python3有,在Python2中使用這個(gè)參數(shù)會(huì)報(bào)錯(cuò)。如果文件是在Windows中創(chuàng)建的,并且使用UTF-8打開文件出現(xiàn)了亂碼,可以把編碼格式改為GBK。使用下面的代碼來(lái)打開text.txt文件:41
文本文件可以按行讀取,也可以直接讀取里面的所有內(nèi)容。
讀取所有行,并以列表的形式返回結(jié)果,代碼如下:
f.readlines()
運(yùn)行效果如圖3-13所示。
圖3-13使用readlines()讀取文本所有行并以列表形式返回結(jié)果文本文件可以按行讀取,也可以直接讀取里面的42
直接把文件里面的全部?jī)?nèi)容用一個(gè)字符串返回,代碼如下:f.read()
運(yùn)行結(jié)果如圖3-14所示。圖3-14直接把整個(gè)文本內(nèi)容以一個(gè)字符串方式返回的結(jié)果直接把文件里面的全部?jī)?nèi)容用一個(gè)字符串返回,代432.使用Python寫文本文件
使用Python寫文件也需要先打開文件,使用如下代碼來(lái)打開文件:withopen('new.txt','w',encoding='utf-8')asf:通過(guò)f來(lái)寫文件
這里多出來(lái)一個(gè)參數(shù)“w”,w是英文write的首字母,意思是以寫的方式打開文件。這個(gè)參數(shù)除了為“w”外,還可以為“a”。它們的區(qū)別在于,如果原來(lái)已經(jīng)有一個(gè)new.txt文件了,使用“w”會(huì)覆蓋原來(lái)的文件,導(dǎo)致原來(lái)的內(nèi)容丟失;而使用“a”,則會(huì)把新的內(nèi)容寫到原來(lái)的文件末尾。2.使用Python寫文本文件44
寫文件時(shí)可以直接寫一大段文本,也可以寫一個(gè)列表。
直接將一大段字符串寫入到文本中,可以使用下面這一行代碼:f.write("一大段文字")
把列表里面的所有字符串寫入到文本中,可以使用下面這一行代碼:f.writelines(['第一段話','第二段話','第三段話'])寫文件時(shí)可以直接寫一大段文本,也可以寫一45
需要特別注意,寫列表的時(shí)候,Python寫到文本中的文字是不會(huì)自動(dòng)換行的,需要人工輸入換行符才可以。代碼和運(yùn)行生成的文本new.txt如圖3-15和圖3-16所示。請(qǐng)注意代碼第8行列表中的兩個(gè)字符串,在new.txt的第3行中被拼在了一起。圖3-15寫字符串和包含字符串的列表到文本中的代碼需要特別注意,寫列表的時(shí)候,Python寫46圖3-16寫文本生成的文件內(nèi)容結(jié)果圖3-16寫文本生成的文件內(nèi)容結(jié)果473.2.2使用Python讀/寫CSV文件CSV文件可以用Excel或者Numbers打開,得到可讀性很高的表格,如圖3-17所示。圖3-17使用Numbers打開CSV文件3.2.2使用Python讀/寫CSV文件C48CSV文件本質(zhì)上就是文本文件,但是如果直接用文本編輯器打開,可讀性并不高,如圖3-18所示。圖3-18直接用文本編輯器打開CSV文件CSV文件本質(zhì)上就是文本文件,但是如果直接49Python自帶操作CSV的模塊。使用這個(gè)模塊,可以將CSV文件的內(nèi)容轉(zhuǎn)換為Python的字典,從而方便使用。1.Python讀CSV文件
要讀取CSV文件,首先需要導(dǎo)入Python的CSV模塊:importcsvPython自帶操作CSV的模塊。使用這個(gè)50
由于CSV文件本質(zhì)上是一個(gè)文本文件,所以需要先以文本文件的方式打開,再將文件對(duì)象傳遞給CSV模塊:withopen('result.csv',encoding='utf-8')asf:reader=csv.DictReader(f)forrowinreader:
print(row)
運(yùn)行結(jié)果圖3-19所示。由于CSV文件本質(zhì)上是一個(gè)文本文件,所以需51圖3-19使用CSV模塊打開CSV文件圖3-19使用CSV模塊打開CSV文件52
代碼中,for循環(huán)得到的row是OrderedDict(有序字典),可以直接像普通字典那樣使用:username=row['username']content=row['content']reply_time=row['reply_time']
運(yùn)行結(jié)果如圖3-20所示。
代碼中,for循環(huán)得到的row是Order53圖3-20像讀字典一樣讀CSV文件圖3-20像讀字典一樣讀CSV文件54
短短幾行代碼,已經(jīng)將CSV文件轉(zhuǎn)換為字典了。
特別注意:
讀取文本內(nèi)容的代碼必須放在縮進(jìn)內(nèi)部進(jìn)行,否則會(huì)導(dǎo)致報(bào)錯(cuò),如圖3-21所示。圖3-21讀取文本內(nèi)容的代碼必須放在縮進(jìn)的里面短短幾行代碼,已經(jīng)將CSV文件轉(zhuǎn)換為字典了55
這是因?yàn)閒變量里面的值是一個(gè)生成器,生成器只有在被使用(更準(zhǔn)確的說(shuō)法是被迭代)的時(shí)候才會(huì)去讀文本內(nèi)容。但是退出with的縮進(jìn)以后,文件就被Python關(guān)閉了,這個(gè)時(shí)候當(dāng)然什么都讀不了。
那有沒(méi)有什么辦法可以繞過(guò)這個(gè)限制呢?當(dāng)然是有的,那就是使用列表推導(dǎo)式。圖3-22所示為使用列表推導(dǎo)式讀取文本內(nèi)容。請(qǐng)對(duì)比圖3-21和圖3-22第4行的不同。這是因?yàn)閒變量里面的值是一個(gè)生成器,生成器只56圖3-22使用列表推導(dǎo)式讀取文本內(nèi)容圖3-22使用列表推導(dǎo)式讀取文本內(nèi)容572.Python寫CSV文件Python可以把一個(gè)字典寫成CSV文件,或者把一個(gè)包含字典的列表寫成CSV文件。Python寫CSV文件比讀CSV文件稍微復(fù)雜一點(diǎn),因?yàn)橐付忻?。列名要和字典的Key一一對(duì)應(yīng)。
Python寫CSV文件時(shí)需要用到csv.DictWriter()這個(gè)類。它接收兩個(gè)參數(shù):第1個(gè)參數(shù)是文件對(duì)象f;第2個(gè)參數(shù)名為fieldnames,值為字典的Key列表。
寫入CSV文件的列名行:
writer.writeheader()2.Python寫CSV文件58
將包含字典的列表全部寫入到CSV文件中:writer.writerows(包含字典的列表)
寫入一個(gè)包含字典的列表,每一個(gè)字典對(duì)應(yīng)CSV的一行。這些字典的Key必須和fieldnames相同。字典可以是普通的無(wú)序字典,所以不需要關(guān)心字典里面Key的順序,但是不能存在fieldnames里面沒(méi)有的Key,也不能缺少fieldnames里面已有的Key。
將包含字典的列表全部寫入到CSV文件中:59
寫入單個(gè)字典:writer.writerow(字典)
代碼如圖3-23所示,運(yùn)行結(jié)果如圖3-24所示。MacOS的Numbers顯示CSV文件的結(jié)果和Excel中顯示的結(jié)果可能存在差異,但是表格里面的數(shù)據(jù)應(yīng)該是一致的。
寫入單個(gè)字典:60圖3-23將包含列表的字典寫入到CSV文件中圖3-23將包含列表的字典寫入到CSV文件中61圖3-24生成的CSV文件用Numbers打開以后的結(jié)果圖3-24生成的CSV文件用Numbers打開以后的結(jié)果623.3階段案例——半自動(dòng)爬蟲開發(fā)
所謂半自動(dòng)爬蟲,顧名思義就是一半手動(dòng)一半自動(dòng)地進(jìn)行爬蟲,手動(dòng)的部分是把網(wǎng)頁(yè)的源代碼復(fù)制下來(lái),自動(dòng)的部分是通過(guò)正則表達(dá)式把其中的有效信息提取出來(lái)。3.3階段案例——半自動(dòng)爬蟲開發(fā)所謂半633.3.1需求分析在百度貼吧中任意尋找一個(gè)貼吧并打開一個(gè)熱門帖子,將帖子的源代碼復(fù)制下來(lái),并保存為source.txt。Python讀入這個(gè)source.txt文件,通過(guò)正則表達(dá)式獲取用戶名、發(fā)帖內(nèi)容和發(fā)帖時(shí)間,并保存為result.csv。涉及的知識(shí)點(diǎn)如下。(1)在瀏覽器中查看網(wǎng)站的源代碼。(2)使用Python讀文本文件。(3)正則表達(dá)式的應(yīng)用。(4)先抓大再抓小的匹配技巧。(5)使用Python寫CSV文件。3.3.1需求分析在百度貼吧中任意尋找一個(gè)貼643.3.2核心代碼構(gòu)建1.在瀏覽器中獲取網(wǎng)頁(yè)的源代碼以Chrome瀏覽器為例來(lái)說(shuō)明如何查看網(wǎng)頁(yè)的源代碼。在網(wǎng)頁(yè)上單擊鼠標(biāo)右鍵,選擇“顯示網(wǎng)頁(yè)源代碼”命令,如圖3-25所示。圖3-25選擇“顯示網(wǎng)頁(yè)源代碼”命令3.3.2核心代碼構(gòu)建1.在瀏覽器中獲取網(wǎng)頁(yè)的源代碼圖365
網(wǎng)頁(yè)源代碼如圖3-26所示。這里可以復(fù)制全部的源代碼,并粘貼到記事本中。圖3-26網(wǎng)頁(yè)源代碼網(wǎng)頁(yè)源代碼如圖3-26所示。這里可以復(fù)制全662.獲取關(guān)鍵信息
要獲取網(wǎng)站的關(guān)鍵信息,就需要觀察網(wǎng)頁(yè)源代碼的規(guī)律。
通過(guò)對(duì)比每一層樓的帖子,可以發(fā)現(xiàn)規(guī)律,即每一層樓都是從“username”開頭的,如圖3-27所示。圖3-27用戶名的規(guī)律2.獲取關(guān)鍵信息圖3-27用戶名的規(guī)律67
先來(lái)看用戶名,從圖3-27可以看出,用戶名符合這樣的規(guī)律:username="(.*?)"
再來(lái)看發(fā)帖內(nèi)容,從圖3-28可以看出,發(fā)帖內(nèi)容符合如下規(guī)律:d_post_contentj_d_post_content">(.*?)<圖3-28發(fā)帖內(nèi)容的規(guī)律先來(lái)看用戶名,從圖3-27可以看出,用戶名68
最后來(lái)看發(fā)帖時(shí)間。請(qǐng)注意,從圖3-29中可以看出,發(fā)帖時(shí)間需要應(yīng)用3.1.3小節(jié)所提到的“括號(hào)內(nèi)和括號(hào)外”技巧。由于方框框住的兩段內(nèi)容有著相同的開頭字符串,如何把“2017-03-1819:39”提取出來(lái),但不要把“15樓”提取出來(lái)?這種情況下,對(duì)于正則表達(dá)式,應(yīng)該在括號(hào)里面包含一些其他的要素,才能只把時(shí)間提取出來(lái)。最后來(lái)看發(fā)帖時(shí)間。請(qǐng)注意,從圖3-29中可以看出69圖3-29發(fā)帖時(shí)間的規(guī)律圖3-29發(fā)帖時(shí)間的規(guī)律70
由于發(fā)帖時(shí)間總是以年開頭的,因此可以在括號(hào)里面包含“2017”,這樣就可以得到正確的年份信息。匹配年份的正則表達(dá)式如下:tail-info">(2017.*?)<由于發(fā)帖時(shí)間總是以年開頭的,因此可以在括號(hào)713.3.3調(diào)試與運(yùn)行完整的半自動(dòng)爬蟲代碼如圖3-30所示,生成的CSV文件如圖3-31所示。圖3-30完整的半自動(dòng)爬蟲代碼3.3.3調(diào)試與運(yùn)行完整的半自動(dòng)爬蟲代碼如圖72圖3-31爬蟲生成的CSV文件圖3-31爬蟲生成的CSV文件73圖3-32把帖子的每一層樓看作一個(gè)塊圖3-32把帖子的每一層樓看作一個(gè)塊74
開始和結(jié)束標(biāo)志如圖3-33所示。
對(duì)原來(lái)的代碼進(jìn)行修改,可以得到邏輯更加合理的新代碼,如圖3-34所示。圖3-33一層樓的開始和結(jié)束標(biāo)志開始和結(jié)束標(biāo)志如圖3-33所示。圖3-3375圖3-34更合乎邏輯的半自動(dòng)爬蟲代碼圖3-34更合乎邏輯的半自動(dòng)爬蟲代碼763.4本章小結(jié)
本章主要講到了正則表達(dá)式和Python的文件操作。
正則表達(dá)式用來(lái)在一大段文字中提取需要的內(nèi)容,用得最多的組合是“(.*?)”。這個(gè)組合可以解決絕大多數(shù)的目標(biāo)提取問(wèn)題。
使用Python讀/寫文本文件和CSV文件都需要先把文件打開,在Python中使用open這個(gè)關(guān)鍵字來(lái)打開文件。在Python中,使用CSV這個(gè)內(nèi)置的模塊可以非常方便地把CSV文件轉(zhuǎn)換成Python的字典,或者把Python的字典轉(zhuǎn)換為CSV文件。
3.4本章小結(jié)本章主要講到了正則表達(dá)式773.5動(dòng)手實(shí)踐
在百度貼吧中尋找一個(gè)自己喜歡的貼吧,將其中一篇熱門帖子的每層樓的發(fā)帖人、發(fā)帖內(nèi)容和發(fā)帖時(shí)間抓取下來(lái)。3.5動(dòng)手實(shí)踐在百度貼吧中尋找一個(gè)自己喜78
PythonCrawlerDevelopment極客學(xué)院J互聯(lián)網(wǎng)+職業(yè)技能系列
Python爬蟲開發(fā)從入門到實(shí)戰(zhàn)(微課版)人民郵電出版社謝乾坤
著
PythonCrawlerDevelopmen79第3章正則表達(dá)式與文件操作
在爬蟲的開發(fā)中,需要把有用的信息從一大段文本中提取出來(lái)。正則表達(dá)式是提取信息的方法之一。
正則表達(dá)式雖然不是最簡(jiǎn)單的也不是最高效的數(shù)據(jù)提取方法,但它是最直接的。而且在某些情況下,只有使用正則表達(dá)式才能達(dá)到目的。學(xué)好正則表達(dá)式,是開發(fā)爬蟲的第一步。第3章正則表達(dá)式與文件操作在爬蟲的開發(fā)80
通過(guò)這一章的學(xué)習(xí),你將會(huì)掌握如下知識(shí)。(1)正則表達(dá)式的基本符號(hào)。(2)如何在Python中使用正則表達(dá)式。(3)正則表達(dá)式的提取技巧。(4)Python讀寫文本文件和CSV文件。通過(guò)這一章的學(xué)習(xí),你將會(huì)掌握如下知識(shí)813.1正則表達(dá)式
正則表達(dá)式(RegularExpression)是一段字符串,它可以表示一段有規(guī)律的信息。Python自帶一個(gè)正則表達(dá)式模塊,通過(guò)這個(gè)模塊可以查找、提取、替換一段有規(guī)律的信息。
在程序開發(fā)中,要讓計(jì)算機(jī)程序從一大段文本中找到需要的內(nèi)容,就可以使用正則表達(dá)式來(lái)實(shí)現(xiàn)。
使用正則表達(dá)式有如下步驟。(1)尋找規(guī)律。(2)使用正則符號(hào)表示規(guī)律。(3)提取信息。3.1正則表達(dá)式正則表達(dá)式(Regul821.點(diǎn)號(hào)“.”
一個(gè)點(diǎn)號(hào)可以代替除了換行符以外的任何一個(gè)字符,包括但不限于英文字母、數(shù)字、漢字、英文標(biāo)點(diǎn)符號(hào)和中文標(biāo)點(diǎn)符號(hào)。2.星號(hào)“*”
一個(gè)星號(hào)可以表示它前面的一個(gè)子表達(dá)式(普通字符、另一個(gè)或幾個(gè)正則表達(dá)式符號(hào))0次到無(wú)限次。3.1.1正則表達(dá)式的基本符號(hào)1.點(diǎn)號(hào)“.”3.1.1正則表達(dá)式的基本符號(hào)833.問(wèn)號(hào)“?”
問(wèn)號(hào)表示它前面的子表達(dá)式0次或者1次。注意,這里的問(wèn)號(hào)是英文問(wèn)號(hào)。
4.反斜杠“\”
反斜杠在正則表達(dá)式里面不能單獨(dú)使用,甚至在整個(gè)Python里都不能單獨(dú)使用。反斜杠需要和其他的字符配合使用來(lái)把特殊符號(hào)變成普通符號(hào),把普通符號(hào)變成特殊符號(hào)。3.問(wèn)號(hào)“?”84
反斜杠不僅可以把特殊符號(hào)變成普通符號(hào),還可以把普通符號(hào)變成特殊符號(hào)。
例如“n”只是一個(gè)普通的字母,但是“\n”代表?yè)Q行符。
在Python開發(fā)中,經(jīng)常遇到的轉(zhuǎn)義字符,如表3-1所示。反斜杠不僅可以把特殊符號(hào)變成普通符號(hào),還可85轉(zhuǎn)義字符意義\n換行符\t制表符\\普通的反斜杠\'單引號(hào)\"雙引號(hào)\d數(shù)字
表3-1常見(jiàn)的轉(zhuǎn)義字符轉(zhuǎn)義字符意義\n換行符\t制表符\\普通的反斜杠\'單引號(hào)\865.?dāng)?shù)字“\d”
正則表達(dá)式里面使用“\d”來(lái)表示一位數(shù)字。為什么要用字母d呢?因?yàn)閐是英文“digital(數(shù)字)”的首字母。
再次強(qiáng)調(diào)一下,“\d”雖然是由反斜杠和字母d構(gòu)成的,但是要把“\d”看成一個(gè)正則表達(dá)式符號(hào)整體。
6.小括號(hào)“()”
小括號(hào)可以把括號(hào)里面的內(nèi)容提取出來(lái)。5.?dāng)?shù)字“\d”873.1.2在Python中使用正則表達(dá)式Python已經(jīng)自帶了一個(gè)功能非常強(qiáng)大的正則表達(dá)式模塊。使用這個(gè)模塊可以非常方便地通過(guò)正則表達(dá)式來(lái)從一大段文字中提取有規(guī)律的信息。Python的正則表達(dá)式模塊名字為“re”,也就是“regularexpression”的首字母縮寫。在Python中需要首先導(dǎo)入這個(gè)模塊再進(jìn)行使用。導(dǎo)入的語(yǔ)句為:importre3.1.2在Python中使用正則表達(dá)式Pyt881.findallPython的正則表達(dá)式模塊包含一個(gè)findall方法,它能夠以列表的形式返回所有滿足要求的字符串。
findall的函數(shù)原型為:re.findall(pattern,string,flags=0)pattern表示正則表達(dá)式,string表示原來(lái)的字符串,flags表示一些特殊功能的標(biāo)志。
1.findall89findall的結(jié)果是一個(gè)列表,包含了所有的匹配到的結(jié)果。如果沒(méi)有匹配到結(jié)果,就會(huì)返回空列表,如圖3-1所示。圖3-1findall返回的內(nèi)容findall的結(jié)果是一個(gè)列表,包含了所有90
當(dāng)需要提取某些內(nèi)容的時(shí)候,使用小括號(hào)將這些內(nèi)容括起來(lái),這樣才不會(huì)得到不相干的信息。如果包含多個(gè)“(.*?)”怎么返回呢?如圖3-2所示,返回的仍然是一個(gè)列表,但是列表里面的元素變?yōu)榱嗽M,元組里面的第1個(gè)元素是賬號(hào),第2個(gè)元素為密碼。圖3-2多個(gè)括號(hào)內(nèi)的內(nèi)容會(huì)以元組形式返回當(dāng)需要提取某些內(nèi)容的時(shí)候,使用小括號(hào)將這些91
請(qǐng)注意代碼中的冒號(hào)和逗號(hào),圖3-1代碼中為中文冒號(hào)和中文逗號(hào);圖3-2代碼中為英文冒號(hào)和英文逗號(hào)。在實(shí)際使用正則表達(dá)式的過(guò)程中,中英文標(biāo)點(diǎn)符號(hào)混淆常常會(huì)導(dǎo)致各種問(wèn)題。特別是冒號(hào)、逗號(hào)和引號(hào),雖然中英文看起來(lái)非常相似,但實(shí)際上中文冒號(hào)和英文冒號(hào)是不一樣的,中文逗號(hào)和英文逗號(hào)也是不一樣的。
在某些字體里面,這種差異甚至無(wú)法察覺(jué),因此在涉及正則表達(dá)式中的標(biāo)點(diǎn)符號(hào)時(shí),最好直接復(fù)制粘貼,而不要手動(dòng)輸入。請(qǐng)注意代碼中的冒號(hào)和逗號(hào),圖3-1代碼中為中92
函數(shù)原型中有一個(gè)flags參數(shù)。這個(gè)參數(shù)是可以省略的。當(dāng)不省略的時(shí)候,具有一些輔助功能,例如忽略大小寫、忽略換行符等。這里以忽略換行符為例來(lái)進(jìn)行說(shuō)明,如圖3-3所示。圖3-3使用re.S作為flag來(lái)忽略換行符函數(shù)原型中有一個(gè)flags參數(shù)。這個(gè)參數(shù)是93
在爬蟲的開發(fā)過(guò)程中非常容易出現(xiàn)這樣的情況,要匹配的內(nèi)容存在換行符“\n”。要忽略換行符,就需要使用到“re.S”這個(gè)flag。雖然說(shuō)匹配到的結(jié)果中出現(xiàn)了“\n”這個(gè)符號(hào),不過(guò)總比什么都得不到強(qiáng)。內(nèi)容里面的換行符在后期清洗數(shù)據(jù)的時(shí)候把它替換掉即可。在爬蟲的開發(fā)過(guò)程中非常容易出現(xiàn)這樣的情況,942.searchsearch()的用法和findall()的用法一樣,但是search()只會(huì)返回第1個(gè)滿足要求的字符串。一旦找到符合要求的內(nèi)容,它就會(huì)停止查找。對(duì)于從超級(jí)大的文本里面只找第1個(gè)數(shù)據(jù)特別有用,可以大大提高程序的運(yùn)行效率。
search()的函數(shù)原型為:re.search(pattern,string,flags=0)2.search95
對(duì)于結(jié)果,如果匹配成功,則是一個(gè)正則表達(dá)式的對(duì)象;如果沒(méi)有匹配到任何數(shù)據(jù),就是None。
如果需要得到匹配到的結(jié)果,則需要通過(guò).group()這個(gè)方法來(lái)獲取里面的值,如圖3-4所示。圖3-4使用.group()來(lái)獲取search()方法找到的結(jié)果對(duì)于結(jié)果,如果匹配成功,則是一個(gè)正則表達(dá)式96
只有在.group()里面的參數(shù)為1的時(shí)候,才會(huì)把正則表達(dá)式里面的括號(hào)中的結(jié)果打印出來(lái)。.group()的參數(shù)最大不能超過(guò)正則表達(dá)式里面括號(hào)的個(gè)數(shù)。參數(shù)為1表示讀取第1個(gè)括號(hào)中的內(nèi)容,參數(shù)為2表示讀取第2個(gè)括號(hào)中的內(nèi)容,以此類推,如圖3-5所示。
只有在.group()里面的參數(shù)為1的時(shí)候,97圖3-5.group()的參數(shù)意義圖3-5.group()的參數(shù)意義983.“.*”和“.*?”的區(qū)別
在爬蟲開發(fā)中,.*?這3個(gè)符號(hào)大多數(shù)情況下一起使用。
點(diǎn)號(hào)表示任意非換行符的字符,星號(hào)表示匹配它前面的字符0次或者任意多次。所以“.*”表示匹配一串任意長(zhǎng)度的字符串任意次。這個(gè)時(shí)候必須在“.*”的前后加其他的符號(hào)來(lái)限定范圍,否則得到的結(jié)果就是原來(lái)的整個(gè)字符串。
如果在“.*”的后面加一個(gè)問(wèn)號(hào),變成“.*?”,那么可以得到什么樣的結(jié)果呢?問(wèn)號(hào)表示匹配它前面的符號(hào)0次或者1次。于是.*?的意思就是匹配一個(gè)能滿足要求的最短字符串。
3.“.*”和“.*?”的區(qū)別99
這樣說(shuō)起來(lái)還是非常抽象,下面通過(guò)一個(gè)實(shí)際的例子來(lái)進(jìn)行說(shuō)明。請(qǐng)看下面這一段話:我的微博密碼是:1234567,QQ密碼是:33445566,銀行卡密碼是:888888,Github密碼是:999abc999,幫我記住它們
這段話有一個(gè)顯著的規(guī)律,即密碼是:xxxxxx,”,也就是在“密碼是”這3個(gè)漢字的后面跟一個(gè)中文的冒號(hào),冒號(hào)后面是密碼,密碼后面是中文的逗號(hào)。
這樣說(shuō)起來(lái)還是非常抽象,下面通過(guò)一個(gè)實(shí)際的例100
如果想把這4個(gè)密碼提取出來(lái),可以構(gòu)造以下兩個(gè)正則表達(dá)式:密碼是:(.*),密碼是:(.*?),
配合Python的findall方法,得到結(jié)果如圖3-6圖所示。
如果想把這4個(gè)密碼提取出來(lái),可以構(gòu)造以下101圖3-6使用“.*”和“.*?”返回的結(jié)果圖3-6使用“.*”和“.*?”返回的結(jié)果102
使用“(.*)”得到的是只有一個(gè)元素的列表,里面是一個(gè)很長(zhǎng)的字符串。
使用第2個(gè)正則表達(dá)式“(.*?)”,得到的結(jié)果是包含4個(gè)元素的列表,每個(gè)元素直接對(duì)應(yīng)原來(lái)文本中的每個(gè)密碼。
一句話總結(jié)如下。①“.*”:貪婪模式,獲取最長(zhǎng)的滿足條件的字符串。②“.*?”:非貪婪模式,獲取最短的能滿足條件的字符串。使用“(.*)”得到的是只有一個(gè)元素的列表1033.1.3正則表達(dá)式提取技巧
1.不需要compile網(wǎng)上很多人的文章中,正則表達(dá)式使用pile()這個(gè)方法,導(dǎo)致代碼變成下面這樣:importreexample_text='我是kingname,我的微博賬號(hào)是:kingname,密碼是:12345678,QQ賬號(hào)是:99999,密碼是:890abcd,銀行卡賬號(hào)是:000001,密碼是:654321,Github賬號(hào)是:99999@,密碼是:7777love8888,請(qǐng)記住他們。'new_pattern=pile('賬號(hào)是:(.*?),密碼是:(.*?),',re.S)user_pass=re.findall(new_pattern,example_text)print(user_pass)3.1.3正則表達(dá)式提取技巧
1.不需要compile104這種寫法雖然結(jié)果正確,但純粹是畫蛇添足,是對(duì)Python的正則表達(dá)式模塊沒(méi)有理解透徹的體現(xiàn),是從其他啰嗦的編程語(yǔ)言中帶來(lái)的壞習(xí)慣。如果閱讀Python的正則表達(dá)式模塊的源代碼,就可以看出pile()是完全沒(méi)有必要的。對(duì)比pile()和re.findall()在源代碼中的寫法,如圖3-7所示的兩個(gè)方框。這種寫法雖然結(jié)果正確,但純粹是畫蛇添足,是對(duì)Pyt105圖3-7Python正則表達(dá)式模塊中的re.findall()和pile()圖3-7Python正則表達(dá)式模塊中的re.findal106
使用pile()的時(shí)候,程序內(nèi)部調(diào)用的是_compile()方法;當(dāng)使用re.finall()的時(shí)候,在模塊內(nèi)部自動(dòng)先調(diào)用了_compile()方法,再調(diào)用findall()方法。re.findall()自帶pile()的功能,所以沒(méi)有必要使用pile()。Python3中正則表達(dá)式模塊的源代碼的入口文件為re.py。這個(gè)文件里面的注釋就是學(xué)習(xí)Python正則表達(dá)式模塊非常好的文檔,它包含了正則表達(dá)式各種符號(hào)的簡(jiǎn)單說(shuō)明和這個(gè)模塊內(nèi)部各個(gè)方法的使用,如圖3-8所示。
使用pile()的時(shí)候,程序內(nèi)部107圖3-8Pythonre.py文件自帶的文檔圖3-8Pythonre.py文件自帶的文檔1082.先抓大再抓小
一些無(wú)效內(nèi)容和有效內(nèi)容可能具有相同的規(guī)則。這種情況下很容易把有效內(nèi)容和無(wú)效內(nèi)容混在一起,如下面這段文字:
有效用戶:姓名:張三姓名:李四姓名:王五2.先抓大再抓小109無(wú)效用戶:姓名:不知名的小蝦米姓名:隱身的張大俠
有效用戶和無(wú)效用戶的名字前面都以“姓名:”開頭,如果使用“姓名:(.*?)\n”來(lái)進(jìn)行匹配,就會(huì)把有效信息和無(wú)效信息混在一起,難以區(qū)分,如圖3-9所示。無(wú)效用戶:110圖3-9使用“姓名:(.*?)\n”導(dǎo)致有效內(nèi)容和無(wú)效內(nèi)容混在一起圖3-9使用“姓名:(.*?)\n”導(dǎo)致有效內(nèi)容和無(wú)效111
要解決這個(gè)問(wèn)題,就需要使用先抓大再抓小的技巧。先把有效用戶這個(gè)整體匹配出來(lái),再?gòu)挠行в脩衾锩嫫ヅ涑鋈嗣a和運(yùn)行效果如圖3-10所示。先抓大再抓小的思想會(huì)貫穿整個(gè)爬蟲開發(fā)過(guò)程,一定要重點(diǎn)掌握。圖3-10代碼和運(yùn)行效果要解決這個(gè)問(wèn)題,就需要使用先抓大再抓小的技巧1123.括號(hào)內(nèi)和括號(hào)外
在上面的例子中,括號(hào)和“.*?”都是一起使用的,因此可能會(huì)有讀者認(rèn)為括號(hào)內(nèi)只能有這3種字符,不能有其他普通的字符。但實(shí)際上,括號(hào)內(nèi)也可以有其他字符,對(duì)匹配結(jié)果的影響如圖3-11所示。圖3-11括號(hào)里有無(wú)其他字符對(duì)匹配結(jié)果的影響3.括號(hào)內(nèi)和括號(hào)外圖3-11括號(hào)里有無(wú)其他字符對(duì)匹配結(jié)果1133.2Python文件操作Python的文件操作涉及對(duì)文件的讀/寫與編碼的處理,是學(xué)習(xí)爬蟲的必備知識(shí)。3.2.1使用Python讀/寫文本文件使用Python來(lái)讀/寫文本需要用到“open”這個(gè)關(guān)鍵字。它的作用是打開一個(gè)文件,并創(chuàng)建一個(gè)文件對(duì)象。使用Python打開文件,有兩種寫法。第1種方式如下:f=open('文件路徑','文件操作方式',encoding='utf-8')對(duì)文件進(jìn)行操作f.close()3.2Python文件操作Python的文件操114第2種方式,使用Python的上下文管理器:withopen('文件路徑','文件操作方式',encoding='utf-8')asf:
對(duì)文件進(jìn)行操作第1種方式需要手動(dòng)關(guān)閉文件,但是在程序開發(fā)中經(jīng)常會(huì)出現(xiàn)忘記關(guān)閉文件的情況。第2種方法不需要手動(dòng)關(guān)閉文件,只要代碼退出了縮進(jìn),Python就會(huì)自動(dòng)關(guān)閉文件。第2種方式,使用Python的上下文管理器:1151.使用Python讀文本文件
使用Python打開一個(gè)文本文件時(shí),首先要保證這個(gè)文件是存在的。在讀文件的時(shí)候,“文件操作方式”這個(gè)參數(shù)可以省略,也可以寫成“r”,也就是read的首字母。
文件路徑可以是絕對(duì)路徑,也可以是相對(duì)路徑。如果是絕對(duì)路徑,Linux和MacOS不能直接使用“~”表示“home目錄”,因?yàn)镻ython不認(rèn)識(shí)“~”這個(gè)符號(hào)。如果非要使用這個(gè)符號(hào),需要使用Python的“os”模塊,代碼如下:importosreal_path=os.path.expanduser('~/project/xxx')1.使用Python讀文本文件116
這樣,Python就會(huì)將這種風(fēng)格的路徑轉(zhuǎn)化為Python能認(rèn)識(shí)的絕對(duì)路徑。
相對(duì)路徑是文本文件相對(duì)于現(xiàn)在的工作區(qū)而言的路徑,并不總是相對(duì)于當(dāng)前正在運(yùn)行的這個(gè)Python文件的路徑。在本章中,請(qǐng)讀者直接將文本文件和Python文件放在一起,這樣就可以直接使用文件名來(lái)打開文本文件。
文本文件的內(nèi)容和它相對(duì)于.py文件的位置如圖3-12所示。這樣,Python就會(huì)將這種風(fēng)格的路徑轉(zhuǎn)化為117圖3-12文本文件的內(nèi)容和它相對(duì)于.py文件的位置圖3-12文本文件的內(nèi)容和它相對(duì)于.py文件的位置118
使用下面的代碼來(lái)打開text.txt文件:withopen('text.txt',encoding='utf-8')asf:通過(guò)f來(lái)讀文件
這里有一個(gè)參數(shù)“encoding”。這個(gè)參數(shù)特別有用,它可以在打開文件的時(shí)候?qū)⑽募D(zhuǎn)換為UTF-8編碼格式,從而避免亂碼的出現(xiàn)。這個(gè)參數(shù)只有Python3有,在Python2中使用這個(gè)參數(shù)會(huì)報(bào)錯(cuò)。如果文件是在Windows中創(chuàng)建的,并且使用UTF-8打開文件出現(xiàn)了亂碼,可以把編碼格式改為GBK。使用下面的代碼來(lái)打開text.txt文件:119
文本文件可以按行讀取,也可以直接讀取里面的所有內(nèi)容。
讀取所有行,并以列表的形式返回結(jié)果,代碼如下:
f.readlines()
運(yùn)行效果如圖3-13所示。
圖3-13使用readlines()讀取文本所有行并以列表形式返回結(jié)果文本文件可以按行讀取,也可以直接讀取里面的120
直接把文件里面的全部?jī)?nèi)容用一個(gè)字符串返回,代碼如下:f.read()
運(yùn)行結(jié)果如圖3-14所示。圖3-14直接把整個(gè)文本內(nèi)容以一個(gè)字符串方式返回的結(jié)果直接把文件里面的全部?jī)?nèi)容用一個(gè)字符串返回,代1212.使用Python寫文本文件
使用Python寫文件也需要先打開文件,使用如下代碼來(lái)打開文件:withopen('new.txt','w',encoding='utf-8')asf:通過(guò)f來(lái)寫文件
這里多出來(lái)一個(gè)參數(shù)“w”,w是英文write的首字母,意思是以寫的方式打開文件。這個(gè)參數(shù)除了為“w”外,還可以為“a”。它們的區(qū)別在于,如果原來(lái)已經(jīng)有一個(gè)new.txt文件了,使用“w”會(huì)覆蓋原來(lái)的文件,導(dǎo)致原來(lái)的內(nèi)容丟失;而使用“a”,則會(huì)把新的內(nèi)容寫到原來(lái)的文件末尾。2.使用Python寫文本文件122
寫文件時(shí)可以直接寫一大段文本,也可以寫一個(gè)列表。
直接將一大段字符串寫入到文本中,可以使用下面這一行代碼:f.write("一大段文字")
把列表里面的所有字符串寫入到文本中,可以使用下面這一行代碼:f.writelines(['第一段話','第二段話','第三段話'])寫文件時(shí)可以直接寫一大段文本,也可以寫一123
需要特別注意,寫列表的時(shí)候,Python寫到文本中的文字是不會(huì)自動(dòng)換行的,需要人工輸入換行符才可以。代碼和運(yùn)行生成的文本new.txt如圖3-15和圖3-16所示。請(qǐng)注意代碼第8行列表中的兩個(gè)字符串,在new.txt的第3行中被拼在了一起。圖3-15寫字符串和包含字符串的列表到文本中的代碼需要特別注意,寫列表的時(shí)候,Python寫124圖3-16寫文本生成的文件內(nèi)容結(jié)果圖3-16寫文本生成的文件內(nèi)容結(jié)果1253.2.2使用Python讀/寫CSV文件CSV文件可以用Excel或者Numbers打開,得到可讀性很高的表格,如圖3-17所示。圖3-17使用Numbers打開CSV文件3.2.2使用Python讀/寫CSV文件C126CSV文件本質(zhì)上就是文本文件,但是如果直接用文本編輯器打開,可讀性并不高,如圖3-18所示。圖3-18直接用文本編輯器打開CSV文件CSV文件本質(zhì)上就是文本文件,但是如果直接127Python自帶操作CSV的模塊。使用這個(gè)模塊,可以將CSV文件的內(nèi)容轉(zhuǎn)換為Python的字典,從而方便使用。1.Python讀CSV文件
要讀取CSV文件,首先需要導(dǎo)入Python的CSV模塊:importcsvPython自帶操作CSV的模塊。使用這個(gè)128
由于CSV文件本質(zhì)上是一個(gè)文本文件,所以需要先以文本文件的方式打開,再將文件對(duì)象傳遞給CSV模塊:withopen('result.csv',encoding='utf-8')asf:reader=csv.DictReader(f)forrowinreader:
print(row)
運(yùn)行結(jié)果圖3-19所示。由于CSV文件本質(zhì)上是一個(gè)文本文件,所以需129圖3-19使用CSV模塊打開CSV文件圖3-19使用CSV模塊打開CSV文件130
代碼中,for循環(huán)得到的row是OrderedDict(有序字典),可以直接像普通字典那樣使用:username=row['username']content=row['content']reply_time=row['reply_time']
運(yùn)行結(jié)果如圖3-20所示。
代碼中,for循環(huán)得到的row是Order131圖3-20像讀字典一樣讀CSV文件圖3-20像讀字典一樣讀CSV文件132
短短幾行代碼,已經(jīng)將CSV文件轉(zhuǎn)換為字典了。
特別注意:
讀取文本內(nèi)容的代碼必須放在縮進(jìn)內(nèi)部進(jìn)行,否則會(huì)導(dǎo)致報(bào)錯(cuò),如圖3-21所示。圖3-21讀取文本內(nèi)容的代碼必須放在縮進(jìn)的里面短短幾行代碼,已經(jīng)將CSV文件轉(zhuǎn)換為字典了133
這是因?yàn)閒變量里面的值是一個(gè)生成器,生成器只有在被使用(更準(zhǔn)確的說(shuō)法是被迭代)的時(shí)候才會(huì)去讀文本內(nèi)容。但是退出with的縮進(jìn)以后,文件就被Python關(guān)閉了,這個(gè)時(shí)候當(dāng)然什么都讀不了。
那有沒(méi)有什么辦法可以繞過(guò)這個(gè)限制呢?當(dāng)然是有的,那就是使用列表推導(dǎo)式。圖3-22所示為使用列表推導(dǎo)式讀取文本內(nèi)容。請(qǐng)對(duì)比圖3-21和圖3-22第4行的不同。這是因?yàn)閒變量里面的值是一個(gè)生成器,生成器只134圖3-22使用列表推導(dǎo)式讀取文本內(nèi)容圖3-22使用列表推導(dǎo)式讀取文本內(nèi)容1352.Python寫CSV文件Python可以把一個(gè)字典寫成CSV文件,或者把一個(gè)包含字典的列表寫成CSV文件。Python寫CSV文件比讀CSV文件稍微復(fù)雜一點(diǎn),因?yàn)橐付忻A忻妥值涞腒ey一一對(duì)應(yīng)。
Python寫CSV文件時(shí)需要用到csv.DictWriter()這個(gè)類。它接收兩個(gè)參數(shù):第1個(gè)參數(shù)是文件對(duì)象f;第2個(gè)參數(shù)名為fieldnames,值為字典的Key列表。
寫入CSV文件的列名行:
writer.writeheader()2.Python寫CSV文件136
將包含字典的列表全部寫入到CSV文件中:writer.writerows(包含字典的列表)
寫入一個(gè)包含字典的列表,每一個(gè)字典對(duì)應(yīng)
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024-2030年中國(guó)團(tuán)購(gòu)行業(yè)當(dāng)前經(jīng)濟(jì)形勢(shì)及投資建議研究報(bào)告
- 2024-2030年中國(guó)噴水織機(jī)行業(yè)供需趨勢(shì)及投資策略分析報(bào)告
- 2024年物流外包合作協(xié)議增補(bǔ)3篇
- 2024年煤炭交易市場(chǎng)誠(chéng)信體系建設(shè)購(gòu)銷運(yùn)輸合同范本3篇
- 2024年版針對(duì)配偶出軌的婚姻解除合同版B版
- 微專題蓋斯定律的高階應(yīng)用-2024高考化學(xué)一輪考點(diǎn)擊破
- 呂梁職業(yè)技術(shù)學(xué)院《數(shù)字營(yíng)銷》2023-2024學(xué)年第一學(xué)期期末試卷
- 2024年某城市關(guān)于垃圾分類處理服務(wù)合同
- 2024年物業(yè)項(xiàng)目托管合同
- 漯河食品職業(yè)學(xué)院《移動(dòng)營(yíng)銷設(shè)計(jì)》2023-2024學(xué)年第一學(xué)期期末試卷
- 淮陰工學(xué)院《供應(yīng)鏈管理3》2022-2023學(xué)年第一學(xué)期期末試卷
- 2025年計(jì)算機(jī)等級(jí)考試一級(jí)計(jì)算機(jī)基礎(chǔ)及MS Office應(yīng)用試卷及解答參考
- 小學(xué)五年級(jí)上冊(cè)語(yǔ)文 第一單元 語(yǔ)文要素閱讀(含解析)
- 2024年廣東公需科目答案
- ABB工業(yè)機(jī)器人基礎(chǔ)知識(shí)
- 中國(guó)校服產(chǎn)業(yè)挑戰(zhàn)與機(jī)遇分析報(bào)告 2024
- 2022版義務(wù)教育物理課程標(biāo)準(zhǔn)
- 山東省日照市2023-2024學(xué)年七年級(jí)上學(xué)期期末數(shù)學(xué)試題(含答案)
- 上海華東師大二附中2025屆高一數(shù)學(xué)第一學(xué)期期末檢測(cè)試題含解析
- 新教科版六年級(jí)上冊(cè)科學(xué)全冊(cè)知識(shí)點(diǎn)(期末總復(fù)習(xí)資料)
- 《靜女》《涉江采芙蓉》對(duì)比閱讀教學(xué)設(shè)計(jì) 2023-2024學(xué)年統(tǒng)編版高中語(yǔ)文必修上冊(cè)
評(píng)論
0/150
提交評(píng)論