---
# System prepended metadata

title: 爬蟲ptt

---

# 爬蟲ptt

## 主要工具
 - import requests
#pip3 install requests
 - BeautifulSoup
#pip3 install beautifulsoup4
 - lxml
#pip3 install lxml

## 建置與執行

### 環境設定
建議使用 virtualenv 控制 python 環境

``` sh
pip3 install virtualenv
which python3
virtualenv -p /usr/bin/python3 Envpython3 #指定python版本並建置envname
source Envpython3/bin/activate
```

### 開始爬蟲

```python=
import requests
from bs4 import BeautifulSoup
homepage = requests.get('https://www.ptt.cc/bbs/hotboards.html')
# print(homepage.text)
# with open ('a.text','a') as f:
#     f.write(homepage.text)
soup = BeautifulSoup(homepage.text,'lxml')
# print(soup)
# with open ('b.text','a') as f:
#     f.write(soup.text)

posts11 = soup.find('a', class_= 'board')
posts12 = soup.select_one('a.board')
print(posts11)
print('---')
print(posts12)

posts1 = soup.find_all('a', class_= 'board')
# posts1 = soup.select('a.board')

for p in posts1:
    h_name = p.find('div', class_= 'board-name')
    # posts2 = soup.select('a.board')
    # print(name)
    h_page = p.select('span')
    # print(page)
    h_classname = p.select('div.board-class')
    # print(classname)
    h_title = p.select('div.board-title')
    # print(title)
    h_url = 'https://www.ptt.cc' + p['href']
    print(h_url)
    article = requests.get(
            url = h_url,
            cookies = {'over18': 'yes'}  # ptt18歲的認證
        )
    soup = BeautifulSoup(article.text,'lxml')
    r_ent = soup.select('div.r-ent')[0].text
    a_url = soup.select('div.title > a')[0]['href']
    a_title = soup.select('div.title')[0].text
    a_author = soup.select('div.author')[0].text
    a_date = soup.select('div.date')[0].text
    
    print(a_title)
    print('https://www.ptt.cc/'+a_url)

```
### lxml格式
![](https://i.imgur.com/EVjUBAw.png)
```python
import requests
from bs4 import BeautifulSoup
import time
start = time.time()
homepage = requests.get('https://www.ptt.cc/bbs/hotboards.html')
# print(homepage.text)
a = 0
b = 0
c = 0
d = 0
for i in range(1,100):
    with open ('nosoup.text','a') as f:
        f.write(homepage.text)
    end = time.time()
    elapsed = end - start
    a = a + elapsed
    print("Time taken nosoup: ", elapsed, "seconds.")

    start = time.time()
    soup = BeautifulSoup(homepage.text,'lxml')
    # print(soup)
    with open ('lxml.text','a') as f:
        f.write(soup.text)
    end = time.time()
    elapsed = end - start
    b = b + elapsed
    print("Time taken lxml.text: ", elapsed, "seconds.")    

    start = time.time()
    h5soup = BeautifulSoup(homepage.text,'html5lib')
    # print(h5soup)
    with open ('html5lib.text','a') as f:
        f.write(h5soup.text)
    end = time.time()
    elapsed = end - start
    c = c + elapsed
    print("Time taken html5lib: ", elapsed, "seconds.")    

    start = time.time()    
    pasersoup = BeautifulSoup(homepage.text,'html.parser')
    # print(pasersoup)
    with open ('htmlparser.text','a') as f:
        f.write(pasersoup.text)

    end = time.time()
    elapsed = end - start
    d = d + elapsed
    print("Time taken htmlparser: ", elapsed, "seconds.")

print("Time taken nosoup: ", a, "seconds.")
print("Time taken lxml: ", b, "seconds.")
print("Time taken html5lib: ", c, "seconds.")
print("Time taken htmlparser: ", d, "seconds.")
```

![](https://i.imgur.com/ycMGaSe.png)

```python
soup = BeautifulSoup(homepage.text,'lxml')
```
![](https://i.imgur.com/AOiL8dN.png)

![](https://i.imgur.com/uZtc49a.png)
```python
posts1 = soup.find_all('a', class_= 'board')
# posts1 = soup.select('a.board')

for p in posts1:
    h_name = p.find('div', class_= 'board-name')
    h_page = p.select('span')
    h_classname = p.select('div.board-class')
    h_title = p.select('div.board-title')
    h_url = 'https://www.ptt.cc' + p['href']
```
![](https://i.imgur.com/IkGyBLT.png)

### 下一頁 爬取版內文章連結與資訊

![](https://i.imgur.com/MVnEF6x.png)
### 解決需要click的頁面

![](https://i.imgur.com/lbAaPAM.png)
### 尋找需要帶入的參數

![](https://i.imgur.com/czPUa2d.png)

```python
article = requests.get(
        url = h_url,
        cookies = {'over18': 'yes'}  # ptt18歲的認證
)
```
#### 有問題就來找我問！
### 作業1
Gossiping 15907 綜合 ◎【八卦】紅．橙．黃．綠．藍．紫
全部
![](https://i.imgur.com/90ue1Ih.png)
### 作業2
去高雄找女網友 要帶多少錢  seabox  5/22
標題 , 作者 , 日期
5頁
![](https://i.imgur.com/VvME5OJ.png)
### 作業3
內文即可 可用split 進行分割 例如: a = 123.345 a.split('.')[0]就可以取得123
另外字串處理有許多方法 replace , strip...等
![](https://i.imgur.com/NqvEYHv.png)

###### 第一個做完的獲得飲料一杯!!

## python class function 

### dict格式
```
dictname = {
"key":Value
}
```

### 作業123 + 存入csv檔
```python=
import requests
import csv
import codecs
from bs4 import BeautifulSoup

class ppt_spider():
    start_urls = ['https://www.ptt.cc/bbs/hotboards.html']

    def __init__(self, start_urls):
        self.h_number = 0
        self.c_number = 0
        self.header = []
        self.start_urls = start_urls
    def start_requests(self):
        header = ['firstpage', 'username', 'classname', 'category_pages', 'category_title', 'category_url', 'a_title', 'a_author', 'a_date', 'note_url', 'note',]
        with open ('ptt.csv','a',encoding='utf-8') as f:
            csv_write = csv.DictWriter(f, fieldnames=header ,delimiter = ';')
            csv_write.writeheader()
            f.close()

        for url in self.start_urls:
            request_url = requests.get(url)
            self.home_page(request_url)

    def home_page(self, response):
        soup = BeautifulSoup(response.text,'lxml')
        posts1 = soup.find_all('a', class_= 'board')
        for p in posts1:
            self.h_number = self.h_number + 1
            username = p.find('div', class_= 'board-name').text
            # print(name)
            category_pages = p.select('span')[0].text
            # print(pages)
            classname = p.select('div.board-class')[0].text
            # print(classname)
            category_title = p.select('div.board-title')[0].text
            # print(title)
            category_url = 'https://www.ptt.cc' + p['href']
            # print(self.h_number, ' : ' , url)
            request_url = requests.get(
                url = category_url,
                cookies = {'over18': 'yes'}  # ptt18歲的認證
            )
            meta1 = {
                "firstpage" : str(response.url),
                "username" : str(username),
                "classname" : str(classname),
                "category_pages" : str(category_pages),
                "category_title" : str(category_title),
                "category_url" : str(category_url)
            }
            self.category_page(request_url, meta1)
    def category_page(self, response, meta1):
            self.c_number = self.c_number + 1
            soup = BeautifulSoup(response.text,'lxml')
            r_ent = soup.select('div.r-ent')[0].text
            a_url = soup.select('div.title > a')[0]['href']
            a_title = soup.select('div.title')[0].text.replace('\n','').replace('\t','')
            if '刪除' in a_title:
                a_url = a_title
                note = a_title
            else :
                note = ''

            a_author = soup.select('div.author')[0].text
            # print(a_author)
            a_date = soup.select('div.date')[0].text
            # print(a_date)
            note_url = 'https://www.ptt.cc/' + a_url

            # print(a_title)
            # print(self.c_number , ' : ' ,'https://www.ptt.cc/' + a_url)
            meta2 = {
                "a_title" : str(a_title),
                "a_author" : str(a_author),
                "a_date" : str(a_date),
                "note_url" : str(note_url),
                "note" : str(note)
            }
            meta_12 = dict(meta1.items() | meta2.items())
            request_url = requests.get(
                url = note_url,
                cookies = {'over18': 'yes'}  # ptt18歲的認證
            )
            self.note(request_url, meta_12)
    def note(self, response, meta_12):
        soup = BeautifulSoup(response.text,'lxml')
        checkpage = soup.title.text
        if '404 Not Found' in checkpage:
            pass
        else:
            meta_12["note"] = soup.select('#main-content')[0].text.split('※ 發信站')[0].replace('\n',' ')

        header = ['firstpage', 'username', 'classname', 'category_pages', 'category_title', 'category_url', 'a_title', 'a_author', 'a_date', 'note_url', 'note']
        with open ('ptt.csv','a',encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=header, delimiter = ';')

            meta_12 = {key: value for key, value in meta_12.items()if key in header}
            writer.writerow(meta_12)


a = ppt_spider()
a.start_requests()
```
### 將dict寫入csv方法
###### 定義fieldnames 欄位名稱 , 根據dict的key做定義 
```python=
header = ['firstpage', 'username', 'classname', 'category_pages', 'category_title', 'category_url', 'a_title', 'a_author', 'a_date', 'note_url', 'note',]
with open ('ptt.csv','a',encoding='utf-8') as f:
            csv_write = csv.DictWriter(f, fieldnames=header ,delimiter = ';')
            csv_write.writeheader()
            meta_12 = {key: value for key, value in meta_12.items()if key in header}
            writer.writerow(meta_12)
            f.close()
```
```python=
meta_12 = {key: value for key, value in meta_12.items()if key in header}
writer.writerow(meta_12)

for key, value in meta_12.items():
    if key in header:
        meta_12 = {key : value}
        writer.writerow(meta_12)
```
## crawl 工具
- python3
- scrapy
 ```pip3 install scrapy```
### scrapy getting started

scrapy startproject Newjob #jobname
cd Newjob/ && scrapy genspider ppt www.ptt.cc

#### 參數解釋
    name = 'ppt'
    scrapy crawl 使用 name 找到spider
![](https://i.imgur.com/SNnDCoc.png)


#### 將要輸出資料的key定義一個scrapy類型
![](https://i.imgur.com/OtN2a4Y.png)
``` 執行scrapy crawl ppt -o name.csv```


### 作業4

http://quotes.toscrape.com/

```python=
import scrapy
from bs4 import BeautifulSoup
from ppt_crawler.items import PptCrawlerItem

class PptSpider(scrapy.Spider):
    name = 'ppt'
    allowed_domains = ['www.ptt.cc']
    start_urls = ['https://www.ptt.cc/bbs/hotboards.html']

    def __init__(self)
        self.h_number = 0
        self.c_number = 0

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(url, callback=self.home_page)
            # request_url = requests.get(url)
            # self.home_page(request_url)

    def home_page(self, response):
        soup = BeautifulSoup(response.text,'lxml')
        posts1 = soup.find_all('a', class_= 'board')
        for p in posts1:
            self.h_number = self.h_number + 1
            username = p.find('div', class_= 'board-name').text
            # print(name)
            category_pages = p.select('span')[0].text
            # print(pages)
            classname = p.select('div.board-class')[0].text
            # print(classname)
            category_title = p.select('div.board-title')[0].text
            # print(title)
            category_url = 'https://www.ptt.cc' + p['href']
            # print(self.h_number, ' : ' , url)
            # request_url = requests.get(
            #     url = category_url,
            #     cookies = {'over18': 'yes'}  # ptt18歲的認證
            # )
            meta = {
                "firstpage" : str(response.url),
                "username" : str(username),
                "classname" : str(classname),
                "category_pages" : str(category_pages),
                "category_title" : str(category_title),
                "category_url" : str(category_url)
            }
            yield scrapy.Request(category_url, callback=self.category_page, meta = meta)
            # self.category_page(request_url, meta1)
    def category_page(self, response):
        meta1 = response.meta
        self.c_number = self.c_number + 1
        soup = BeautifulSoup(response.text,'lxml')
        r_ent = soup.select('div.r-ent')[0].text
        a_url = soup.select('div.title > a')[0]['href']
        a_title = soup.select('div.title')[0].text.replace('\n','').replace('\t','')
        if '刪除' in a_title:
            a_url = a_title
            note = a_title
        else :
            note = ''

        a_author = soup.select('div.author')[0].text
        # print(a_author)
        a_date = soup.select('div.date')[0].text
        # print(a_date)
        note_url = 'https://www.ptt.cc/' + a_url

        # print(a_title)
        # print(self.c_number , ' : ' ,'https://www.ptt.cc/' + a_url)
        meta2 = {
            "a_title" : str(a_title),
            "a_author" : str(a_author),
            "a_date" : str(a_date),
            "note_url" : str(note_url),
            "note" : str(note)
        }
        meta = dict(meta1.items() | meta2.items())
        # print(meta)
        # request_url = requests.get(
        #     url = note_url,
        #     cookies = {'over18': 'yes'}  # ptt18歲的認證
        # )
        # self.note(request_url, meta_12)
        yield scrapy.Request(note_url, cookies = {'over18': 'yes'}, callback=self.note, meta = meta)
    def note(self, response):
        meta = response.meta
        soup = BeautifulSoup(response.text,'lxml')
        checkpage = soup.title.text
        if '404 Not Found' in checkpage:
            pass
        else:
            meta["note"] = soup.select('#main-content')[0].text.split('※ 發信站')[0].replace('\n',' ')
        
        item = PptCrawlerItem()
        item['firstpage'] = meta['firstpage']
        item['username'] = meta['username']
        item['classname'] = meta['classname']
        item['category_pages'] = meta['category_pages']
        item['category_title'] = meta['category_title']
        item['category_url'] = meta['category_url']
        item['a_title'] = meta['a_title']
        item['a_author'] = meta['a_author']
        item['a_date'] = meta['a_date']
        item['note_url'] = meta['note_url']
        item['note'] = meta['note']
        return item
```