整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          利用 Python 爬取網站的新手指南 - Linu

          利用 Python 爬取網站的新手指南 - Linux 中國

          過基本的 Python 工具獲得爬取完整 HTML 網站的實踐經驗。

          ? 來源:linux.cn ? 作者:Julia Piaskowski ? 譯者:Hacker ?

          (本文字數:11235,閱讀時長大約:14 分鐘)

          有很多很棒的書可以幫助你學習 Python ,但是誰真正讀了這那些大部頭呢?(劇透:反正不是我)。

          許多人覺得教學書籍很有用,但我通常不會從頭到尾地閱讀一本書來學習。我是通過做一個項目,努力的弄清楚一些內容,然后再讀另一本書來學習。因此,暫時丟掉書,讓我們一起學習 Python。

          接下來是我的第一個 Python 爬取項目的指南。它對 Python 和 HTML 的假定知識要求很低。這篇文章旨在說明如何使用 Python 的 requests 庫訪問網頁內容,并使用 BeatifulSoup4 庫以及 JSON 和 pandas 庫解析網頁內容。我將簡要介紹 Selenium 庫,但我不會深入研究如何使用該庫——這個主題值得有自己的教程。最終,我希望向你展示一些技巧和小竅門,以減少網頁爬取過程中遇到的問題。

          安裝依賴

          我的 GitHub 存儲庫 中提供了本指南的所有資源。如果需要安裝 Python3 的幫助,請查看 Linux 、 Windows 和 Mac 的教程。

          $ python3 -m venv
          $ source venv/bin/activate
          $ pip install requests bs4 pandas
          

          如果你喜歡使用 JupyterLab ,則可以使用 notebook 運行所有代碼。 安裝 JupyterLab 有很多方法,這是其中一種:

          # from the same virtual environment as above, run:
          $ pip install jupyterlab
          

          為網站抓取項目設定目標

          現在我們已經安裝了依賴項,但是爬取網頁需要做什么?

          讓我們退一步,確保使目標清晰。下面是成功完成網頁爬取項目需求列表:

          • 我們收集的信息,是值得我們花大力氣去建立一個有效的網頁爬取器的。
          • 我們所下載的信息是可以通過網頁爬取器合法和道德地收集的。
          • 對如何在 HTML 代碼中找到目標信息有一定的了解。
          • 利用恰當的工具:在此情況下,需要使用 BeautifulSoup 庫和 requests 庫。
          • 知道(或愿意去學習)如何解析 JSON 對象。
          • 有足夠的 pandas 數據處理技能。

          關于 HTML 的備注:HTML 是運行在互聯網上的“猛獸”,但我們最需要了解的是標簽的工作方式。標簽是一對由尖括號包圍關鍵詞(一般成對出現,其內容在兩個標簽中間)。比如,這是一個假裝的標簽,稱為 pro-tip:

          <pro-tip> All you need to know about html is how tags work </pro-tip>
          

          我們可以通過調用標簽 pro-tip 來訪問其中的信息(All you need to know…)。本教程將進一步介紹如何查找和訪問標簽。要進一步了解 HTML 基礎知識,請查看 本文 。

          網站爬取項目中要找的是什么

          有些數據利用網站爬取采集比利用其他方法更合適。以下是我認為合適項目的準則:

          沒有可用于數據(處理)的公共 API。通過 API 抓取結構化數據會容易得多,(所以沒有 API )有助于澄清收集數據的合法性和道德性。而有相當數量的結構化數據,并有規律的、可重復的格式,才能證明這種努力的合理性。網頁爬取可能會很痛苦。BeautifulSoup(bs4)使操作更容易,但無法避免網站的個別特殊性,需要進行定制。數據的相同格式化不是必須的,但這確實使事情變得更容易。存在的 “邊際案例”(偏離規范)越多,爬取就越復雜。

          免責聲明:我沒有參加過法律培訓;以下內容無意作為正式的法律建議。

          關于合法性,訪問大量有價值信息可能令人興奮,但僅僅因為它是可能的,并不意味著應該這樣做。

          值得慶幸的是,有一些公共信息可以指導我們的道德規范和網頁爬取工具。大多數網站都有與該網站關聯的 robots.txt 文件,指出允許哪些爬取活動,哪些不被允許。它主要用于與搜索引擎(網頁抓取工具的終極形態)進行交互。然而,網站上的許多信息都被視為公共信息。因此,有人將 robots.txt 文件視為一組建議,而不是具有法律約束力的文檔。 robots.txt 文件并不涉及數據的道德收集和使用等主題。

          在開始爬取項目之前,問自己以下問題:

          • 我是否在爬取版權材料?
          • 我的爬取活動會危害個人隱私嗎?
          • 我是否發送了大量可能會使服務器超載或損壞的請求?
          • 爬取是否會泄露出我不擁有的知識產權?
          • 是否有規范網站使用的服務條款,我是否遵循了這些條款?
          • 我的爬取活動會減少原始數據的價值嗎?(例如,我是否打算按原樣重新打包數據,或者可能從原始來源中抽取網站流量)?

          當我爬取一個網站時,請確??梢詫λ羞@些問題回答 “否”。

          要深入了解這些法律問題,請參閱 2018 年出版的 Krotov 和 Silva 撰寫的 《Web 爬取的合法性和道德性》 和 Sellars 的 《二十年 Web 爬取和計算機欺詐與濫用法案》 。

          現在開始爬取網站

          經過上述評估,我想出了一個項目。我的目標是爬取愛達荷州所有 Family Dollar 商店的地址。 這些商店在農村地區規模很大,因此我想了解有多少家這樣的商店。

          起點是 Family Dollar 的位置頁面

          愛達荷州 Family Dollar 所在地頁面


          首先,讓我們在 Python 虛擬環境中加載先決條件。此處的代碼將被添加到一個 Python 文件(如果你想要個名稱,則為 scraper.py)或在 JupyterLab 的單元格中運行。

          import requests # for making standard html requests
          from bs4 import BeautifulSoup # magical tool for parsing html data
          import json # for parsing data
          from pandas import DataFrame as df # premier library for data organization
          

          接下來,我們從目標 URL 中請求數據。

          page=requests.get("https://locations.familydollar.com/id/")
          soup=BeautifulSoup(page.text, 'html.parser')
          

          BeautifulSoup 將 HTML 或 XML 內容轉換為復雜樹對象。這是我們將使用的幾種常見對象類型。

          • BeautifulSoup —— 解析的內容
          • Tag —— 標準 HTML 標記,這是你將遇到的 bs4 元素的主要類型
          • NavigableString —— 標簽內的文本字符串
          • Comment —— NavigableString 的一種特殊類型

          當我們查看 requests.get() 輸出時,還有更多要考慮的問題。我僅使用 page.text() 將請求的頁面轉換為可讀的內容,但是還有其他輸出類型:

          • page.text() 文本(最常見)
          • page.content() 逐字節輸出
          • page.json() JSON 對象
          • page.raw() 原始套接字響應(對你沒啥用)

          我只在使用拉丁字母的純英語網站上操作。 requests 中的默認編碼設置可以很好地解決這一問題。然而,除了純英語網站之外,就是更大的互聯網世界。為了確保 requests 正確解析內容,你可以設置文本的編碼:

          page=requests.get(URL)
          page.encoding='ISO-885901'
          soup=BeautifulSoup(page.text, 'html.parser')
          

          仔細研究 BeautifulSoup 標簽,我們看到:

          • bs4 元素 tag 捕獲的是一個 HTML 標記。
          • 它具有名稱和屬性,可以像字典一樣訪問:tag['someAttribute']。
          • 如果標簽具有相同名稱的多個屬性,則僅訪問第一個實例。
          • 可通過 tag.contents 訪問子標簽。
          • 所有標簽后代都可以通過 tag.contents 訪問。
          • 你始終可以使用以下字符串:re.compile("your_string") 訪問一個字符串的所有內容,而不是瀏覽 HTML 樹。

          確定如何提取相應內容

          警告:此過程可能令人沮喪。

          網站爬取過程中的提取可能是一個令人生畏的充滿了誤區的過程。我認為解決此問題的最佳方法是從一個有代表性的示例開始然后進行擴展(此原理對于任何編程任務都是適用的)。查看頁面的 HTML 源代碼至關重要。有很多方法可以做到這一點。

          你可以在終端中使用 Python 查看頁面的整個源代碼(不建議使用)。運行此代碼需要你自擔風險:

          print(soup.prettify())
          

          雖然打印出頁面的整個源代碼可能適用于某些教程中顯示的玩具示例,但大多數現代網站的頁面上都有大量內容。甚至 404 頁面也可能充滿了頁眉、頁腳等代碼。

          通常,在你喜歡的瀏覽器中通過 “查看頁面源代碼” 來瀏覽源代碼是最容易的(單擊右鍵,然后選擇 “查看頁面源代碼” )。這是找到目標內容的最可靠方法(稍后我將解釋原因)。

          Family Dollar 頁面源代碼


          在這種情況下,我需要在這個巨大的 HTML 海洋中找到我的目標內容 —— 地址、城市、州和郵政編碼。通常,對頁面源(ctrl+F)的簡單搜索就會得到目標位置所在的位置。一旦我實際看到目標內容的示例(至少一個商店的地址),便會找到將該內容與其他內容區分開的屬性或標簽。

          首先,我需要在愛達荷州 Family Dollar 商店中收集不同城市的網址,并訪問這些網站以獲取地址信息。這些網址似乎都包含在 href 標記中。太棒了!我將嘗試使用 find_all 命令進行搜索:

          dollar_tree_list=soup.find_all('href')
          dollar_tree_list
          

          搜索 href 不會產生任何結果,該死。這可能是因為 href 嵌套在 itemlist 類中而失敗。對于下一次嘗試,請搜索 item_list。由于 class 是 Python 中的保留字,因此使用 class_ 來作為替代。soup.find_all() 原來是 bs4 函數的瑞士軍刀。

          dollar_tree_list=soup.find_all(class_='itemlist')
          for i in dollar_tree_list[:2]:
            print(i)
          

          有趣的是,我發現搜索一個特定類的方法一般是一種成功的方法。通過找出對象的類型和長度,我們可以了解更多有關對象的信息。

          type(dollar_tree_list)
          len(dollar_tree_list)
          

          可以使用 .contents 從 BeautifulSoup “結果集” 中提取內容。這也是創建單個代表性示例的好時機。

          example=dollar_tree_list[2] # a representative example
          example_content=example.contents
          print(example_content)
          

          使用 .attr 查找該對象內容中存在的屬性。注意:.contents 通常會返回一個項目的精確的列表,因此第一步是使用方括號符號為該項目建立索引。

          example_content=example.contents[0]
          example_content.attrs
          

          現在,我可以看到 href 是一個屬性,可以像字典項一樣提取它:

          example_href=example_content['href']
          print(example_href)
          

          整合網站抓取工具

          所有的這些探索為我們提供了前進的路徑。這是厘清上面邏輯的一個清理版本。

          city_hrefs=[] # initialise empty list
          
          for i in dollar_tree_list:
              cont=i.contents[0]
              href=cont['href']
              city_hrefs.append(href)
          
          #  check to be sure all went well
          for i in city_hrefs[:2]:
            print(i)
          

          輸出的內容是一個關于抓取愛達荷州 Family Dollar 商店 URL 的列表。

          也就是說,我仍然沒有獲得地址信息!現在,需要抓取每個城市的 URL 以獲得此信息。因此,我們使用一個具有代表性的示例重新開始該過程。

          page2=requests.get(city_hrefs[2]) # again establish a representative example
          soup2=BeautifulSoup(page2.text, 'html.parser')
          

          Family Dollar 地圖和代碼


          地址信息嵌套在 type="application/ld+json" 里。經過大量的地理位置抓取之后,我開始認識到這是用于存儲地址信息的一般結構。幸運的是,soup.find_all() 開啟了利用 type 搜索。

          arco=soup2.find_all(type="application/ld+json")
          print(arco[1])
          

          地址信息在第二個列表成員中!原來如此!

          使用 .contents 提?。◤牡诙€列表項中)內容(這是過濾后的合適的默認操作)。同樣,由于輸出的內容是一個列表,因此我為該列表項建立了索引:

          arco_contents=arco[1].contents[0]
          arco_contents
          

          喔,看起來不錯。此處提供的格式與 JSON 格式一致(而且,該類型的名稱中確實包含 “json”)。 JSON 對象的行為就像是帶有嵌套字典的字典。一旦你熟悉利用其去工作,它實際上是一種不錯的格式(當然,它比一長串正則表達式命令更容易編程)。盡管從結構上看起來像一個 JSON 對象,但它仍然是 bs4 對象,需要通過編程方式轉換為 JSON 對象才能對其進行訪問:

          arco_json= json.loads(arco_contents)
          
          type(arco_json)
          print(arco_json)
          

          在該內容中,有一個被調用的 address 鍵,該鍵要求地址信息在一個比較小的嵌套字典里。可以這樣檢索:

          arco_address=arco_json['address']
          arco_address
          

          好吧,請大家注意。現在我可以遍歷存儲愛達荷州 URL 的列表:

          locs_dict=[] # initialise empty list
          
          for link in city_hrefs:
            locpage=requests.get(link)   # request page info
            locsoup=BeautifulSoup(locpage.text, 'html.parser')
                # parse the page's content
            locinfo=locsoup.find_all(type="application/ld+json")
                # extract specific element
            loccont=locinfo[1].contents[0]  
                # get contents from the bs4 element set
            locjson=json.loads(loccont)  # convert to json
            locaddr=locjson['address'] # get address
            locs_dict.append(locaddr) # add address to list
          

          用 Pandas 整理我們的網站抓取結果

          我們在字典中裝載了大量數據,但是還有一些額外的無用項,它們會使重用數據變得比需要的更為復雜。要執行最終的數據組織,我們需要將其轉換為 Pandas 數據框架,刪除不需要的列 @type 和 country,并檢查前五行以確保一切正常。

          locs_df=df.from_records(locs_dict)
          locs_df.drop(['@type', 'addressCountry'], axis=1, inplace=True)
          locs_df.head(n=5)
          

          確保保存結果??!

          df.to_csv(locs_df, "family_dollar_ID_locations.csv", sep=",", index=False)
          

          我們做到了!所有愛達荷州 Family Dollar 商店都有一個用逗號分隔的列表。多令人興奮。

          Selenium 和數據抓取的一點說明

          Selenium 是用于與網頁自動交互的常用工具。為了解釋為什么有時必須使用它,讓我們來看一個使用 Walgreens 網站的示例。 “檢查元素” 提供了瀏覽器顯示內容的代碼:


          雖然 “查看頁面源代碼” 提供了有關 requests 將獲得什么內容的代碼:

          如果這兩個不一致,是有一些插件可以修改源代碼 —— 因此,應在將頁面加載到瀏覽器后對其進行訪問。requests 不能做到這一點,但是 Selenium 可以做到。

          Selenium 需要 Web 驅動程序來檢索內容。實際上,它會打開 Web 瀏覽器,并收集此頁面的內容。Selenium 功能強大 —— 它可以通過多種方式與加載的內容進行交互(請閱讀文檔)。使用 Selenium 獲取數據后,繼續像以前一樣使用 BeautifulSoup:

          url="https://www.walgreens.com/storelistings/storesbycity.jsp?requestType=locator&state=ID"
          driver=webdriver.Firefox(executable_path='mypath/geckodriver.exe')
          driver.get(url)
          soup_ID=BeautifulSoup(driver.page_source, 'html.parser')
          store_link_soup=soup_ID.find_all(class_='col-xl-4 col-lg-4 col-md-4')
          

          對于 Family Dollar 這種情形,我不需要 Selenium,但是當呈現的內容與源代碼不同時,我確實會保留使用 Selenium。

          小結

          總之,使用網站抓取來完成有意義的任務時:

          • 耐心一點
          • 查閱手冊(它們非常有幫助)

          如果你對答案感到好奇:

          Family Dollar 位置圖


          美國有很多 Family Dollar 商店。

          完整的源代碼是:

          import requests
          from bs4 import BeautifulSoup
          import json
          from pandas import DataFrame as df
          
          page=requests.get("https://www.familydollar.com/locations/")
          soup=BeautifulSoup(page.text, 'html.parser')
          
          # find all state links
          state_list=soup.find_all(class_='itemlist')
          
          state_links=[]
          
          for i in state_list:
              cont=i.contents[0]
              attr=cont.attrs
              hrefs=attr['href']
              state_links.append(hrefs)
          
          # find all city links
          city_links=[]
          
          for link in state_links:
              page=requests.get(link)
              soup=BeautifulSoup(page.text, 'html.parser')
              familydollar_list=soup.find_all(class_='itemlist')
              for store in familydollar_list:
                  cont=store.contents[0]
                  attr=cont.attrs
                  city_hrefs=attr['href']
                  city_links.append(city_hrefs)
          # to get individual store links
          store_links=[]
          
          for link in city_links:
              locpage=requests.get(link)
              locsoup=BeautifulSoup(locpage.text, 'html.parser')
              locinfo=locsoup.find_all(type="application/ld+json")
              for i in locinfo:
                  loccont=i.contents[0]
                  locjson=json.loads(loccont)
                  try:
                      store_url=locjson['url']
                      store_links.append(store_url)
                  except:
                      pass
          
          # get address and geolocation information
          stores=[]
          
          for store in store_links:
              storepage=requests.get(store)
              storesoup=BeautifulSoup(storepage.text, 'html.parser')
              storeinfo=storesoup.find_all(type="application/ld+json")
              for i in storeinfo:
                  storecont=i.contents[0]
                  storejson=json.loads(storecont)
                  try:
                      store_addr=storejson['address']
                      store_addr.update(storejson['geo'])
                      stores.append(store_addr)
                  except:
                      pass
          
          # final data parsing
          stores_df=df.from_records(stores)
          stores_df.drop(['@type', 'addressCountry'], axis=1, inplace=True)
          stores_df['Store']="Family Dollar"
          
          df.to_csv(stores_df, "family_dollar_locations.csv", sep=",", index=False)
          

          作者注釋:本文改編自 2020 年 2 月 9 日在俄勒岡州波特蘭的 我在 PyCascades 的演講 。


          via: opensource.com

          作者: Julia Piaskowski 選題: lujun9972 譯者: stevenzdg988 校對: wxy

          本文由 LCTT 原創編譯, Linux中國 榮譽推出

          點擊“了解更多”可訪問文內鏈接

          TMl 的標簽可以分為單個標簽和成對標簽。

          單個標簽:html4 規定單個標簽要有一個 / 表示結尾, html5 則不用

          <!--單個標簽-->
          <meta>
          <!--成對標簽 -->
          <div></div>

          以下是HTMl中常用的一些標簽


          div 標簽

          div 標簽 主要用來將相關的內容組合到一塊,就像菜市場把各個蔬菜分成不同種類區分擺放是一個道理。

          div 是最常見也是比較重要的標簽,網頁布局中經常使用的一類標簽。通常布局被稱為 DIV + CSS 布局

          <div>
            div 就是一個分類的存儲箱子
          </div>


          p標簽

          p標簽表示段落, 在網頁文字中應用的比較多

          <!--段落和段落間會換行-->
          <p>第一段</p>
          <p>第二段</p>


          H-標題標簽

          h標簽分為六個

          標簽

          語義

          h1

          一級標題

          h2

          二級標題

          h3

          三級標題

          h4

          四級標題

          h5

          五級標題

          h6

          六級標題

          引用標題標簽后,字體會加粗、字號一會變大


          ul 無序標簽

          無序標簽是沒有顯示順序的列表,無序列表前面通常會有一個“小點”, 這個小點可以用type屬性控制。其中有三個展示方式(不過這種方式比較固定,不夠靈活和美觀, 已經被CSS的效果代替),如下:

          值(type屬性)

          描述

          disc

          默認值,實心圓

          circle

          空心圓

          square

          實心方框

          舉例:

          <!--ul標簽內部只能放置li標簽-->
          <!--li標簽內部可以放其他的標簽-->
          <ul type=">
              <li>無序列表元素1</li> <!--列表項-->
              <li>無序列表元素2</li>
          </ul>
          
          
          
          
          實心圓
          <ul type="disc">
            <li>西紅柿</li>
            <li>黃瓜</li>
          </ul>
          空心圓
          <ul type="circle">
            <li>西紅柿</li>
            <li>黃瓜</li>
          </ul>
          實心方框
          <ul type="square">
            <li>西紅柿</li>
            <li>黃瓜</li>
          </ul>


          ol 有序標簽

          • ol 前面的標簽是有序的,可以是數字、字母、羅馬數字等。同樣控制這些樣式使用的是type屬性。

          type屬性值

          意義

          a

          小寫英文字母編號

          A

          大寫英文字母編號

          i

          小寫羅馬數字編號

          I

          大寫羅馬數字編號

          1

          數字編號(默認)

          • 設置start屬性,表示從哪個編號開始
          • 加 reversed 表示倒敘排列


          有序列表, 從2開始
          <ol start="2">
            <li>元素1</li>
            <li>元素2</li>
          </ol>
          
          
          小寫字母表示
          <ol type="a">
            <li>元素1</li>
            <li>元素2</li>
            <li>元素3</li>
          </ol>
          
          
          倒敘
          <ol reversed>
            <li>元素1</li>
            <li>元素2</li>
            <li>元素3</li>
          </ol>


          dl 自定義列表

          dl標簽表示自定義列表

          dt表示數據項,dd表示數據定義, dd是dt標簽的解釋


          <dl>
              <dt>西紅柿</dt>
              <dd>紅、酸</dd>
              <dt>黃瓜</dt>
              <dd>綠、澀</dd>
            </dl>


          img標簽

          img 用來插入圖片,包括但不限于以下圖片格式

          圖片格式

          備注

          .jpg、.jpeg

          通常用于照片,是一種有損壓縮格式

          .png

          通常用于logo、背景,支持透明和半透明。便攜式網絡圖像

          .svg

          矢量圖片


          <!-- src(source)屬性, 圖片地址,可以為相對路徑,也可以為絕對路徑-->
          <!-- alt 如果遇到圖片無法加載的情況,網頁上會展示 alt的 值 -->
          <!-- width 和 height 表示 寬和高, 如果只設置一個, 那么另外一個就會跟著成比例縮放-->
          <img src="./images/images.jpg" alt="星期一" width="120" height="20">


          a 標簽

          用a標簽來制作超級鏈接

          <!-- href 屬性 表示 其他頁面的鏈接,支持相對路徑和絕對路徑,還可以鏈接到其它網站 -->
          <!--target 屬性表示 打開其他鏈接的方式-->
          <!-- title 屬性表示 鏈接的標題, 當鼠標移動到鏈接上,會展示出來-->
          <a href="http://www.baidu.com" target="blank" title="文字標題">百度</a>
          
          
          
          
          
          
          <!--也可以用a標簽作為錨點 錨點可以是本頁面的錨點,也可以是其他頁面的錨點-->
          <h1 id="title">頭部標題</h1>
          ... 此處省略一些代碼
          <a href="#title">返回標題</a>
          
          
          
          
          <!--下載鏈接,指向 doc, zip, zip等文件格式時,a標簽將成為自動下載鏈接-->
          <a href="./download/halou.zip">發郵件</a>
          <!-- mailto:前綴的鏈接是郵件鏈接,系統將自動打開email相關軟件-->
          <a href="mailto:halouworld@126.com">發郵件</a>
          <!-- tel: 前綴鏈接是電話鏈接,系統將自動打開撥號鍵-->
          <a href="tel:11111111111">打開撥號鍵盤</a>


          audio標簽

          audio標簽用來插入音頻標簽

          <!--添加 controls 后才會顯示 播放控件-->
          <!--常用音頻格式 mp3 和 ogg格式-->
          <!--autoplay 自動播放屬性-->
          <!--loop 屬性表示循環播放-->
          <audio controls src="./video/demo.mp3">
                 您的瀏覽器不支持 audio標簽,請升級
          </audio>
          
          
          
          
          <audio controls src="./video/demo.mp3" autoplay loop>
               您的瀏覽器不支持 audio標簽,請升級
          </audio>

          video標簽

          video 標簽用于插入一段視頻

          <!--有的視頻不能播放 ,詳見 https://blog.csdn.net/weixin_34272308/article/details/94614657 -->
          <!-- controls 顯示視頻播放控件  -->
          <!-- autoplay 自動播放 -->
          <!-- loop 循環播放 -->
          <!-- 常見的 視頻格式 mp4 ogv webm 等-->
          <video controls autoplay loop src="./video/5-4 RDB2.mp4" >
              您的瀏覽器不支持 video標簽,請升級
          </video>


          其它區塊標簽

          以前的區塊標簽只有div,現在為了更好的方便搜索引擎抓取網站,因此有了以下語義更加明確的區塊標簽

          <section>

          文檔的區域,比div語義上還要大一點

          <header>

          頁頭

          <main>

          網頁核心部分

          <footer>

          頁腳


          其他的語義標簽

          • span 標記文本標記區域,沒有特殊效果,結合CSS使用
          • b 標簽 加粗文章(可以使用CSS實現同樣效果)
          • u 加下換線文字
          • br 換行
          • i 傾斜文字(可以使用CSS實現同樣效果)
          • strong 代表特別重要的文字
          • em 需要強調的文字,有一定的傾斜,也可以用其配置CSS做表情文字
          • mark 高亮文字
          • figure 和 figcaption 。figure - 一段獨立的內容 figcaption- figure 內部元素的說明性內容


          表單

          表單用來收集信息并且可以完成和后端的數據傳輸

          表單中大致可以分為三種標簽

          • form標簽,標識表單區域, 內部的元素都可能被表單提取信息
          • input 標簽, 標識輸入、點擊等需要和用戶交互的場景
          • datalist 下拉框,支持搜索,通常和input一塊使用

          一些表單的示例

          <!--action 表示要提交到后端的網址-->
          <!--method 表示表單提交的方式,通常有 get 、 post 、put、delete等-->
          
          
          <form action="/save" meththo="post"></form>
          
          
          <!--<form> 標簽中 input 文本框 type="text" 表示文本框-->
          <!-- value 表示文本框中的值 -->
          <!--planceholder表示提示文字,在沒任何輸入值的情況下,作為提示信息-->
          <!--disabled 表示禁用-->
          <input type="text" value="123" planceholder="提示文字" disabled>
          
          
          <!---單選按鈕,name相等,表示選擇了一個,另一個就不能選擇了-->
          <!--checked 表示默認被選中-->
          <!-- value 屬性表示要提交到后端服務器的值-->
          <input type="radio" name="radio_group" checked>
          <input type="radio"  name="radio_group">
          
          
          
          
          <label>
              <input type="radio" name="sex"> 男
          </label>
          <label> 
              <input type="radio" name="sex"> 女
          </label>
          
          
          
          
          <!--html4 中的標簽 通過for 屬性 和 其他標簽的id屬性進行綁定-->
          
          
          <input type="radio" name="sex" id="nan"> 
          <label for="nan">男</label>
          
          
          <input type="radio" name="sex"   id="nv">
          <label for="nv">女</label>
          
          
          <!--復選框 type="checkbox" 同一組的的復選框,name值應該相同 ,復選框也有value值, 用于向服務器提交數據-->
          <input type="checkbox" name="hobby" value="soccer" > 足球
          <input type="checkbox" name="hobby" value="basket" > 籃球
          
          
          <!--密碼框-->
          <input type="password" placeholder="請輸入密碼">
          
          
          <!-- 下拉菜單 -->
          <select>
            <option value="alipay">支付寶</option>
            <option value="wxpay">微信支付</option>
          </select>
          
          
          <!--多文本框 rows 和  clos 分別用于設置 行數 和 列數-->
          <textarea rows="3" cols="5"></textarea>
          
          
          <!--三種按鈕 submit 提交按鈕  button 普通按鈕 可以簡寫為  <button></button> reset 按鈕 重置按鈕-->
          <input type="button" value="普通按鈕">
              <input type="reset" value="重置按鈕"> 
              <input type="submit" value="提交表單">
          
          
          
          
          <!--像 email 和 url 等格式,如果點擊提交按鈕,不符合格式,會有提示-->
          <form>
              日期空間: <input type="date">  <br/>
              時間空間: <input type="time">  <br/>
              日期時間空間 <input type="datetime-local">  <br/>
          
          
              文件:<input type="file"> <br/>  <br/>
              數字控件: <input type="number"> <br/>
              拖拽條: <input type="range"> <br/>
              搜索框: <input type="search"> <br/>
              網址控件: <input type="url"> <br/>
              郵箱控件: <input type="email" >
              <input type="submit" value="提交">
          </form>
          
          
          
          
          <!-- datalist 備選項示例 -->
          <input type="text" list="province">
          <datalist id="province">
            <option value="陜西"></option>
            <option value="山西"></option>
            <option value="河北"></option>
            <option value="山東"></option>
          </datalist>


          表格

          可以用html渲染表格

          • table 標簽表示表格
          • tr 表示行
          • td 表示單元格
          • caption 表格的標題,通常放在表格的第一行
          • th 表示列標題


          <!--表格示例-->
          <table border="1">
            <caption>我是標題</caption>
            <tr>
                <th>第一列標題</th>
                <th>第二列標題</th>
            </tr>
            <tr>
                <td>第一行第一列</td>
                <td>第一行第二列</td>
            </tr>
            <tr>
                <td>第二行第一列</td>
                <td>第二行第二列</td>
            </tr>
          </table>


          • 單元格的合并, clospan 用來設置td 或者th的列跨度 , rowspan屬性用來設置td或者th的行跨度
          <!--跨列示例-->
          <table border="1">
              <caption>我是標題</caption>
              <tr>
                  <th>第一列標題</th>
                  <th>第二列標題</th>
              </tr>
              <tr>
                  <td colspan="2">跨兩行</td>
              </tr>
              <tr>
                  <td>第二行第一列</td>
                  <td>第二行第二列</td>
              </tr>
          </table>
          
          
          <!--跨行示例-->
          <table border="1">
                  <caption>我是標題</caption>
                  <tr>
                      <th>第一列標題</th>
                      <th>第二列標題</th>
                  </tr>
                  <tr>
                      <td rowspan="2">第一行第一列</td>
                      <td>第一行第二列</td>
                  </tr>
                  <tr>
                      <td>第二行第二列</td>
                  </tr>
                  <tr>
                      <td>第三行第一列</td>
                      <td>第三行第二列</td>
                  </tr>
            </table>


          • 表格的其他標簽 , thead 定義表頭, tbody 定義表格的主題內容, tfoot 標簽定義表格底部,通常用來匯總等

          markdown中寫下你的文章,并使用Python將它們轉換成HTML-作者Florian Dahlitz,于2020年5月18日(15分鐘)

          介紹

          幾個月前,我想開通自己的博客,而不是使用像Medium這樣的網站。這是一個非?;A的博客,所有的文章都是HTML形式的。然而,有一天,我突然產生了自己編寫Markdown到HTML生成器的想法,最終這將允許我用markdown來編寫文章。此外,為它添加諸如估計閱讀時間之類的擴展特性會更容易。長話短說,我實現了自己的markdown到HTML生成器,我真的很喜歡它!

          在本系列文章中,我想向您展示如何構建自己的markdown到HTML生成器。該系列由三部分組成:

          • 第一部分(本文)介紹了整個管線的實現。

          • 第二部分通過一個模塊擴展了實現的管線,該模塊用于計算給定文章的預計閱讀時間。

          • 第三部分演示如何使用管線生成自己的RSS摘要。

          這三部分中使用的代碼都可以在GitHub上找到。

          備注:我的文章中markdown到HTML生成器的想法基于Anthony Shaw文章中的實現。

          項目構建

          為了遵循本文的內容,您需要安裝幾個軟件包。我們把它們放進requirements.txt文件。

          Markdown是一個包,它允許您將markdown代碼轉換為HTML。之后我們用Flask產生靜態文件。

          但在安裝之前,請創建一個虛擬環境,以避免Python安裝出現問題:

          激活后,您可以使用pip安裝requirements.txt中的依賴。

          很好!讓我們創建幾個目錄來更好地組織代碼。首先,我們創建一個app目錄。此目錄包含我們提供博客服務的Flask應用程序。所有后續目錄都將在app目錄內創建。其次,我們創建一個名為posts的目錄。此目錄包含要轉換為HTML文件的markdown文件。接下來,我們創建一個templates目錄,其中包含稍后使用Flask展示的模板。在templates目錄中,我們再創建兩個目錄:

          posts包含生成的HTML文件,這些文件與應用程序根目錄中posts目錄中的文件相對應。

          shared包含在多個文件中使用的HTML文件。

          此外,我們還創建了一個名為services的目錄。該目錄將包含我們在Flask應用程序中使用的模塊,或者為它生成某些東西。最后,創建一個名為static的目錄帶有兩個子目錄images和css。自定義CSS文件和文章的縮略圖將存儲在此處。

          您的最終項目結構應如下所示:

          令人驚嘆!我們完成了一般的項目設置。我們來看看Flask的設置。

          Flask設置

          路由

          我們在上一節安裝了Flask。但是,我們仍然需要一個Python文件來定義用戶可以訪問的端點。在app目錄中創建main.py并將以下內容復制到其中。

          該文件定義了一個具有兩個端點的基礎版Flask應用程序。用戶可以使用/route訪問第一個端點返回索引頁,其中列出了所有文章。

          第二個端點是更通用的端點。它接受post的名稱并返回相應的HTML文件。

          接下來,我們通過向app目錄中添加一個__init__.py,將其轉換為一個Python包。此文件為空。如果您使用UNIX計算機,則可以從項目的根目錄運行以下命令:

          模板

          現在,我們創建兩個模板文件index.html以及layout.html,都存儲在templates/shared目錄中。這個layout.html模板將用于單個博客條目,而index.html模板用于生成索引頁,從中我們可以訪問每個帖子。讓我們從index.html模板開始。

          它是一個基本的HTML文件,其中有兩個元標記、一個標題和兩個樣式表。注意,我們使用一個遠程樣式表和一個本地樣式表。遠程樣式表用于啟用Bootstrap[1]類。第二個是自定義樣式。我們晚點再定義它們。

          HTML文件的主體包含一個容器,其中包含Jinja2[2]邏輯,用于為每個post生成Bootstrap卡片[3]。您是否注意到我們不直接基于變量名訪問這些值,而是需要將[0]添加到其中?這是因為文章中解析的元數據是列表。實際上,每個元數據元素都是由單一元素組成的列表。我們稍后再看。到目前為止,還不錯。讓我們看看layout.html模板。

          如你所見,它比前一個短一點,簡單一點。文件頭與index.html文件很相似,除了我們有不同的標題。當然,我們可以共用一個模板,但是我不想讓事情變得更復雜。

          body中的容器僅定義一個h1標記。然后,我們提供給模板的內容被插入并呈現。

          樣式

          正如上一節所承諾的,我們將查看自定義CSS文件style.css. 我們在static/css中找到該文件,并根據需要自定義頁面。下面是我們將用于基礎示例的內容:

          我不喜歡Bootstrap中blockquotes的默認外觀,所以我們在左側添加了一點間距和邊框。此外,blockquote段落底部的頁邊空白將被刪除。不刪除的話看起來很不自然。

          最后但并非最不重要的是,左右兩邊的填充被刪除。由于兩邊都有額外的填充,縮略圖沒有正確對齊,所以在這里刪除它們。

          到現在為止,一直都還不錯。我們完成了關于Flask的所有工作。讓我們開始寫一些帖子吧!

          寫文章

          正如標題所承諾的,你可以用markdown寫文章-是的!在寫文章的時候,除了保證正確的markdown格式外,沒有其他需要注意的事情。

          在完成本文之后,我們需要在文章中添加一些元數據。此元數據添加在文章之前,并由三個破折號分隔開來---。下面是一個示例文章(post1.md)的摘錄:

          注意:您可以在GitHub庫的app/posts/post1.md中找到完整的示例文章。

          在我們的例子中,元數據由標題、副標題、類別、發布日期和index.html中卡片對應縮略圖的路徑組成.

          我們在HTML文件中使用了元數據,你還記得嗎?元數據規范必須是有效的YAML。示例形式是鍵后面跟著一個冒號和值。最后,冒號后面的值是列表中的第一個也是唯一的元素。這就是我們通過模板中的索引運算符訪問這些值的原因。

          假設我們寫完了文章。在我們可以開始轉換之前,還有一件事要做:我們需要為我們的帖子生成縮略圖!為了讓事情更簡單,只需從你的電腦或網絡上隨機選取一張圖片,命名它為placeholder.jpg并把它放到static/images目錄中。GitHub存儲庫中兩篇文章的元數據包含一個代表圖像的鍵值對,值是placeholder.jpg。

          注意:在GitHub存儲庫中,您可以找到我提到的兩篇示例文章。

          markdown到HTML轉換器

          最后,我們可以開始實現markdown to HTML轉換器。因此,我們使用我們在開始時安裝的第三方包Markdown。我們先創建一個新模塊,轉換服務將在其中運行。因此,我們在service目錄中創建了converter.py。我們一步一步看完整個腳本。您可以在GitHub存儲庫中一次查看整個腳本。

          首先,我們導入所需的所有內容并創建幾個常量:

          ROOT指向我們項目的根。因此,它是包含app的目錄。

          POSTS_DIR是以markdown編寫的文章的路徑。

          TEMPLATE_DIR分別指向對應的templates目錄。

          BLOG_TEMPLATE_文件存儲layout.html的路徑。

          INDEX_TEMPLATE_FILE是index.html

          BASE_URL是我們項目的默認地址,例如。https://florian-dahlitz.de.默認值(如果不是通過環境變量DOMAIN提供的話)是http://0.0.0.0:5000。

          接下來,我們創建一個名為generate_entries的新函數。這是我們定義的唯一一個轉換文章的函數。

          在函數中,我們首先獲取POSTS_DIR目錄中所有markdown文件的路徑。pathlib的awesome glob函數幫助我們實現它。

          此外,我們定義了Markdown包需要使用的擴展。默認情況下,本文中使用的所有擴展都隨它的安裝一起提供。

          注意:您可以在文檔[4]中找到有關擴展的更多信息。

          此外,我們實例化了一個新的文件加載程序,并創建了一個在轉換項目時使用的環境。隨后,將創建一個名為all_posts的空列表。此列表將包含我們處理后的所有帖子?,F在,我們進入for循環并遍歷POSTS_DIR中找到的所有文章。

          我們啟動for循環,并打印當前正在處理的post的路徑。如果有什么東西出問題了,這尤其有用。然后我們就知道,哪個文章的轉換失敗了。

          接下來,我們在默認url之后增加一部分。假設我們有一篇標題為“面向初學者的Python”的文章。我們將文章存儲在一個名為python-for-beginners.md,的文件中,因此生成的url將是http://0.0.0.0:5000/posts/python-for-beginners。

          變量url_html存儲的字符串與url相同,只是我們在末尾添加了.html。我們使用此變量定義另一個稱為target_file.的變量。變量指向存儲相應HTML文件的位置。

          最后,我們定義了一個變量md,它表示markdown.Markdown的實例,用于將markdown代碼轉換為HTML。您可能會問自己,為什么我們沒有在for循環之前實例化這個實例,而是在內部實例化。當然,對于我們這里的小例子來說,這沒有什么區別(只是執行時間稍微短一點)。但是,如果使用諸如腳注之類的擴展來使用腳注,則需要為每個帖子實例化一個新實例,因為腳注添加后就不會從此實例中刪除。因此,如果您的第一篇文章使用了一些腳注,那么即使您沒有明確定義它們,所有其他文章也將具有相同的腳注。

          讓我們轉到for循環中的第一個with代碼塊。

          實際上,with代碼塊打開當前post并將其內容讀入變量content。之后調用_md.convert將以markdown方式寫入的內容轉換為HTML。隨后,env環境根據提供的模板BLOG_TEMPLATE_FILE(即layout.html如果你還記得的話)渲染生成的HTML。

          第二個with 代碼塊用于將第一個with 代碼塊中創建的文檔寫入目標文件。

          以下三行代碼從元數據中獲取發布日期(被發布的日期),將其轉換為正確的格式(RFC 2822),并將其分配回文章的元數據。此外,生成的post_dict被添加到all_posts列表中。

          我們現在出了for循環,因此,我們遍歷了posts目錄中找到的所有posts并對其進行了處理。讓我們看看generate_entries函數中剩下的三行代碼。

          我們按日期倒序對文章進行排序,所以首先顯示最新的文章。隨后,我們將文章寫到模板目錄一個新創建的index.html文件中。別把index.html錯認為templates/shared目錄中的那個。templates/shared目錄中的是模板,這個是我們要使用Flask服務的生成的。

          最后我們在函數generate_entries之后添加以下if語句。

          這意味著如果我們通過命令行執行文件,它將調用generate_entries函數。

          太棒了,我們完成了converter.py腳本!讓我們從項目的根目錄運行以下命令來嘗試:

          您應該看到一些正在轉換的文件的路徑。假設您編寫了兩篇文章或使用了GitHub存儲庫中的兩篇文章,那么您應該在templates目錄中找到三個新創建的文件。首先是index.html,它直接位于templates目錄中,其次是templates/posts目錄中的兩個HTML文件,它們對應于markdown文件。

          最后啟動Flask應用程序并轉到http://0.0.0.0:5000。

          總結

          太棒了,你完成了這個系列的第一部分!在本文中,您已經學習了如何利用Markdown包創建自己的Markdown to HTML生成器。您實現了整個管線,它是高度可擴展的,您將在接下來的文章中看到這一點。

          希望你喜歡這篇文章。一定要和你的朋友和同事分享。如果你還沒有,考慮在Twitter上關注我@DahlitzF或者訂閱我的通知,這樣你就不會錯過任何即將發表的文章。保持好奇心,不斷編碼!

          參考文獻

          Bootstrap (http://getbootstrap.com/)

          Primer on Jinja Templating (https://realpython.com/primer-on-jinja-templating/)

          Bootstrap Card (https://getbootstrap.com/docs/4.4/components/card/)

          Python-Markdown Extensions (https://python-markdown.github.io/extensions/)

          Tweet

          英文原文:https://florian-dahlitz.de/blog/build-a-markdown-to-html-conversion-pipeline-using-python
          譯者:阿布銩

          主站蜘蛛池模板: 日韩人妻无码一区二区三区综合部| 无码人妻aⅴ一区二区三区有奶水| 国产精品盗摄一区二区在线| 亚洲日韩中文字幕一区| 中文字幕一区二区三区人妻少妇| 日本福利一区二区| 亚洲国产精品乱码一区二区| 日韩成人无码一区二区三区| 在线视频一区二区三区三区不卡| 国产精品合集一区二区三区 | 国产一区二区三区在线观看影院| 亚洲第一区精品观看| 亚洲大尺度无码无码专线一区| 日本精品一区二区久久久| 久久久久人妻一区二区三区| 午夜无码视频一区二区三区| 美女视频一区三区网站在线观看| 精品少妇一区二区三区在线 | 亚洲av日韩综合一区二区三区| 午夜无码一区二区三区在线观看 | 国产精品一区二区资源| 无码国产精品一区二区免费16| 久久精品国产第一区二区| 国产精品一区不卡| 精品国产天堂综合一区在线| 色欲综合一区二区三区| 日韩一区二区三区四区不卡| 蜜桃臀无码内射一区二区三区| 久久国产午夜一区二区福利| 精品一区二区三区四区在线| 中文字幕一区二区三区免费视频| 一区三区三区不卡| 精品久久一区二区三区| 无码人妻久久久一区二区三区 | 国产丝袜无码一区二区三区视频| 久久无码一区二区三区少妇| 亚洲线精品一区二区三区| 日韩av无码一区二区三区| 欧美成人aaa片一区国产精品| 无码视频一区二区三区| 亚洲色精品vr一区二区三区|