分類
說說

網站使用Creative Commons授權

前兩天看了《互聯網之子》(The Internet's Own Boy),覺得用Creative Commons挺好於是就用了。這個電影很好下,英語不錯的可以到https://archive.org/details/TheInternetsOwnBoyTheStoryOfAaronSwartz在線看。

其實CC也本地化了各種版本包括臺灣和大陸,關於Creative Commons請大家參考維基百科:知识共享。我這裏用的英文的證書,簡體中文的CC聲明點開是中英混雜的好詭異。另外推薦大家看一下維基百科:公有领域詞條。

分類
程序

用Python抓取大衆點評的用戶評論

大衆點評的知识产权声明可真是霸道啊!還是自己先保存一份。下面代碼先將評論及商戶保存到sqlite數據庫,如果需要還可以導出成CSV,這樣辦公軟件就能直接打開查看了。

from bs4 import BeautifulSoup
import sys,time,random,urllib,http.cookiejar,socket,sqlite3,csv


goOn=1
stopDate=''
UserID=''
review={'shopName':'','shopAddr':'','shopURL':'','reviewURL':'','star':'',
'starDetail':'','costPerPeople':'','reviewText':'','dishes':'','reviewTime':''}

def getHTML(url):
  print("Fetching "+url)
  request = urllib.request.Request(url)
  request.add_header("User-Agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0")
  try:
    response = urllib.request.urlopen(request)
  except (urllib.error.HTTPError, socket.error,urllib.error.URLError) as e:
    print('Connection error occurred when inserting data.'+str(e))
  else:
    if response.code != 200:
      print("Error code:"+response.code)  
    else:
      html = response.read().decode('utf-8')
      return html

def getList(url):
  global review,goOn
  reviewList=getHTML(url)
  soupAll = BeautifulSoup(reviewList).find_all("div",{"class":"txt J_rptlist"})

  for soup in soupAll:
    shopLink = soup.find("a",{"class":"J_rpttitle"})
    review['shopName']=shopLink.text
    review['shopURL']=shopLink.get("href")
    
    shopAddr = soup.find("p",{"class":"col-exp"})
    review['shopAddr']=shopAddr.text
    
    reviewID = soup.find("a",{"class":"J_flower aheart"})
    review['reviewURL']="http://www.dianping.com/review/"+reviewID.get("data-id")
    
    reviewDateDiv = soup.find("div",{"class":"mode-tc info"})
    reviewDateSpan=reviewDateDiv.find("span",{"class":"col-exp"})
    reviewDate=str(reviewDateSpan.text)[3:]
    if(len(reviewDate)==8 and reviewDate>stopDate):
      getReview(review['reviewURL'])
      #抓取頻率
      time.sleep(random.randrange(5,10))
    else:
      goOn=0
  if(goOn==0):
    print("Finished.")
    exit()
    
      
    

def save():
  global review,UserID
  conn = sqlite3.connect('DZDB_'+UserID+'_Reviews.db')
  c = conn.cursor()
  c.execute("""create table if not exists reviews (ID integer primary key not NULL,shopName char(50),shopAddr char(100),shopURL char(100),reviewURL char(100),star char(1),starDetail char(15),costPerPeople char(15),reviewText TEXT,dishes char(100),reviewTime char(20))""")
  s="""insert into reviews (ID,shopName,shopAddr,shopURL,reviewURL,star,starDetail,costPerPeople,reviewText,dishes,reviewTime) VALUES (NULL,\'"""+review['shopName']+'\',\''+review['shopAddr']+'\',\''+review['shopURL']+'\',\''+review['reviewURL']+'\',\''+str(review['star'])+'\',\''+review['starDetail']+'\',\''+review['costPerPeople']+'\',\''+review['reviewText']+'\',\''+review['dishes']+'\',\''+review['reviewTime']+'\')'
  c.execute(s)
  conn.commit()
  c.close
  print("Record at "+review['shopName']+" saved to Datebase.")
  review={'shopName':'','shopAddr':'','shopURL':'','reviewURL':'','star':'',
'starDetail':'','costPerPeople':'','reviewText':'','dishes':'','reviewTime':''}

def getReview(url):
  global review
  reviewHTML=getHTML(url)
  reviewAll=BeautifulSoup(reviewHTML)
  shopInfo= reviewAll.find("ul",{"class":"contList-info"})
  star=str(shopInfo.find("li"))
  if("msstar50" in star):
    review['star']=5
  elif ("msstar40" in star):
    review['star']=4
  elif ("msstar30" in star):
    review['star']=3
  elif ("msstar20" in star):
    review['star']=2
  elif ("msstar10" in star):
    review['star']=1
  else:
    review['star']=0
  starDetails=shopInfo.find_all("span",{"class":"rst"})
  starDetail=""
  for s in starDetails:
    s1=s.text[0:3]
    starDetail=starDetail+s1
  review['starDetail']=starDetail
  
  reviewText= reviewAll.find("div",{"class":"contList-con"})
  review['reviewText']=reviewText.text
  units= reviewAll.find_all("div",{"class":"comment-unit"})
  for unit in units:
    unit=str(unit.text).replace('\n','')
    if("人均:" in unit):    
      review['costPerPeople']=unit[4:]
    elif("喜欢的菜:" in unit): 
      unit=unit.replace(' ','')
      unit=unit.replace('\xa0',' ')
      review['dishes']=unit[7:]
    
  reviewInfo= reviewAll.find("ul",{"class":"contList-fn"})  
  reviewTime=reviewInfo.find("li")
  review['reviewTime']=reviewTime.text
  save() 

def main():
  fun=int(input("请输入数字选择功能:\n[1]抓取数据,[2]导出数据: \n"))
  if(fun==1):
    fetchReview()
  elif(fun==2):
    sqliteToCSV()
  else:
    print("请输入1或2。")

    
def sqliteToCSV():
  dbFile=str(input("请输入数据库文件名:\n"))
  with open(dbFile+'.csv','w+',newline='') as csvfile:
    spamwriter = csv.writer(csvfile)
    conn=sqlite3.connect(dbFile)
    c = conn.cursor()
    spamwriter.writerow(['ID','shopName','shopAddr','shopURL','reviewURL','star',
'starDetail','costPerPeople','reviewText','dishes','reviewTime'])
    for row in c.execute('SELECT * FROM reviews'):
      spamwriter.writerow(row)
    c.close()
    print("CSV文件成功導出。")
    
def fetchReview():
  #抓取参数:用户ID,起始页,结束日期
  global stopDate,UserID
  UserID=str(input("请输入您的大众点评ID,可以在您大众点评主页的网址中看到,如23262500:\n"))
  startPageNo=int(input("开始的页码,如1:\n"))
  stopDate=str(input("请输入评论结束日期(yy-mm-dd),如00-00-00:\n"))
  
  urlBase="http://www.dianping.com/member/"+UserID+"/reviews?pg="
  startPageNo=startPageNo-1
  while(goOn==1):
    startPageNo=startPageNo+1
    getList(urlBase+str(startPageNo))
    
if __name__ == "__main__":
    main()
幾點說明
  • 抓取頻率不要過大,否則大衆點評會屏蔽IP。我在抓取到20頁左右的時候碰到過一次屏蔽IP。如果意外中斷,你可以設置參數繼續下載,附w3school的SQL基礎教程
  • BeautifulSoup真是個好工具,連Qpython3都自帶了,但是遺憾的是這個代碼在Qpython3上跑報NoneType錯誤。
  • 我用了幾次都沒問題。
分類
程序

IP地址和歸屬地

移動2G網速好爛!看個IP歸屬地都要等好久。於是自己寫了個小小的php,輸出用不了1K數據,這樣既省流量又快速。代碼在Github:根據IP查看地理位置


//服務端獲取客戶端IP
$ip = getenv('HTTP_CLIENT_IP')?:
    getenv('HTTP_X_FORWARDED_FOR')?:
    getenv('HTTP_X_FORWARDED')?:
    getenv('HTTP_FORWARDED_FOR')?:
    getenv('HTTP_FORWARDED')?:
    getenv('REMOTE_ADDR');

本文更新於 2016/09/19。

分類
网站

把Laravel5產品放到阿里輕雲

阿里的輕雲服務器滿足Laravel5的基本要求(PHP >= 5.4,Mcrypt PHP Extension,OpenSSL PHP Extension,Mbstring PHP Extension,Tokenizer PHP Extension),於是嘗試着部署了下。首先我是切換到Linux系統的,不知道Win下是什麼狀況。然後在高級環境設置中把php版本設置爲5.5,php.ini全部打開(未測試關閉情況)。然後用FTP上傳文件到服務器。

把除public文件夾外的所有文件上傳到/cgi-bin中,修改storage文件夾及其子文件夾爲可寫權限。把public中所有文件上傳到/htdocs中。修改/htdocs/index.php中如下兩行:

// require __DIR__.'/../bootstrap/autoload.php';
require __DIR__.'/../cgi-bin/bootstrap/autoload.php';

// $app = require_once __DIR__.'/../bootstrap/app.php';
$app = require_once __DIR__.'/../cgi-bin/bootstrap/app.php';

正常情況下,此時應該能正常訪問無數據庫操作的頁面。這裏也是我比較困惑的地方,配置文件.env無法被讀取,感覺是上傳後就遭到服務器刪除了。之前測試.htaccess也怪怪的,還懷疑是不是沒開mod_rewrite。但是我在子文件夾裝wordpress測試就正常,所以.htaccess和mod_rewrite都是正常的。但是.env始終讀取不到,我還試圖修改vendor\vlucas\phpdotenv\src\Dotenv.php也不行,最後直接把數據庫信息寫config/database.php裏了。這樣數據庫就能正常使用了。.env中的其他設置也都可以在config中各個文件單獨設置,當然了如果.env可用還是用.env的好。

分類
說說

明天開始釜山之旅

簽證是在淘寶辦的,便宜而且要的資料少。飛機從香港10點起飛,很趕。廉價航空還是直飛所以值得。酒店都是在Booking上訂的,一個月前就訂了,挺便宜。TripAdvisor上有推薦的景點還有大家的評價,很好。目前就安排了下第二天的行程,剩下的到了在說。谷歌地圖的離線地圖一直都不順手,我試圖保存韓國居然還提示無法保存,於是下了個MAPS.ME離線地圖,感覺還不錯,有點費電,但可以做到完全不依賴網絡。在網上買個一個香港韓國兩地手機數據卡,八十多元,7天5G流量。谷歌翻譯下了個韓語翻譯包,還下了韓語輸入法以及幾個韓國旅遊軟件。


行程

28日。提前一天用滴滴預約了出租車,趕在06:30關口開門前到達。06:55B3準點從深圳灣開往屯門碼頭,換上了之前買的上網卡,很好用。07:38到達屯門碼頭,渡輪08:00從屯門碼頭到東沖碼頭。不知爲什麼船07:53就出發了,於是08:18就到了東沖碼頭,並順利打到出租車。08:28到達機場。當地時間14:09下飛機,比香港早一個小時。建議在機場多取些現金,因爲機場和火車站單筆最多可取3,000,000.00,普通7-11單筆都是1,000,000.00。但是不取也沒關係,因爲韓國很多很多地方都支持信用卡罰款,哪怕是個很小的小館子。7-11直接買個公交卡是很必要的。輕鐡轉地鐵就到了酒店,期間坐錯了一次方向,必須要出站才能去另一個方向。汽車酒店豪華程度再次刷新我們的酒店記錄。房間內不僅有電腦還有雙人按摩浴缸!不僅提供了方便麪還提供了神油……晚上在附近已經小館子吃的泡飯和熱湯冷面,挺好吃的。就是一般的家庭飯店,老闆的對象是中國人,所以懂一點漢語,否則還真是很難溝通。

29日。之前在TripAdvisor上看到甘川文化村很漂亮,於是就過來看。天上下着小雨,這裏總共不超過10個遊客。這裏很漂亮,但是釜山有很多這麼漂亮的地方。如果人太多肯定會影響觀景體驗。然後做17路公交去了Songdo Beach,沿海邊的棧道走到Amnam Park的最南端。最後坐7路公交返回酒店。艱難的在酒店電腦上訂了去慶州的火車票,因爲韓文的操作系統很難用,後來發現其實用手機完全可以輕鬆搞定。訂票成功後把需要打印的頁面另存爲pdf,到火車站出示給售票員即可,不必打印出來。另外提前訂票有優惠,越早越優惠。另外需要說的你查釜山到慶州的火車是沒有的,需要查釜田或海雲臺到慶州才行。我們坐的無窮花號,三四十人民幣一個人。KTX比較快,兩百多人民幣一個人。

30日。我們坐100-1公交去海雲臺。沿Dongbaek Park轉一圈,看了APEC展覽館,最後到海雲臺沙灘。在海雲臺沙灘到海雲臺地鐵站的路上遇到一個類似臺灣夜市的地方,貌似很多小吃。到了海雲臺地鐵站才發現海雲臺火車站已經遷了新址,看了下地圖不近也不遠,於是決定走路去。最後順利找到車站,一個嶄新的現代化的火車站。但是火車站附近比較荒涼,沒有吃飯的地方,我們取完票往回走,吃了拌冷面和炸豬排。拌冷麪挺辣的,阿姨還特意給我加了此白菜絲解辣。吃完飯我們沿着西邊的小河走到Daecheon Park,沿途也是非常漂亮,水特別清,公園裏有水壩和瀑布。然後從Jangsan-ro南側的小路回到了火車站。17:00到達慶州車站,17:18步行到民宿。由於之前現金取的少,民俗阿姨特別爽快的讓我們明天再付。民宿有免費自行車。晚上吃得海鮮火鍋,火鍋好辣和冰鎮啤酒真是絕配!

01日。上午從火車站對面坐500路去了南山,也有可愛的墳墓,小松鼠和溪水。下山時走了錯路,比較艱險,但是也很有樂趣。甚至有一段需要用繩子,手腳並用類似攀巖那樣下。下來後她腳都磨破了,還好帶了創可貼。回來還是坐的500但是在大陵苑就下了,之後還步行去了石冰庫、雁鴨池最後走回了民宿。雁鴨池旁邊的荷花池有個亭子,看夕陽超美!晚餐吃了烤肉,好多肉,很好吃,雖然兩個人吃了150,但是覺得很值。

02日。阿姨強烈推薦去良洞村,還親自送我們到了公交站。阿姨英文口音很重,直到公交車開過來我才明白她說的是203路。天氣特別好,村子雖然古樸但是很有生機,確實非常值得來一趟。我們到的時候比較早,人也比較少。非常悠閒的一直逛到下午才走。逛的那麼慢還有一個原因是我們買了一瓶帶氣的十幾度米酒,喝的暈暈的。在下車的地方3點有一班回去的公交車。下車時從火車站多坐一站就到了慶州的商業區,東西普遍都挺貴的。回民宿的途中看到一個菜市場,裏面乾淨整潔無異味無噪音。在裏面吃了點粉條和紫菜卷,關東煮的湯可以隨便喝。然後又在超市買了瓶啤酒和雪糕。

03日。每天民俗的阿姨都做早餐給我們吃,第一天和第三天是雞蛋餅,昨天是湯餃。上午十點的火車回釜田。中午在京城麵家吃了拌冷面和拌飯,爲什麼這家寫了名字其他家都沒寫呢,因爲其他家都是韓語的名字,實在不知道是什麼。安頓好後去了Centum City,很多大商場有點像茂業。最後在下面的超市逛了逛,在美食廣場吃了麵片湯。

04日。坐2路到民主公園,下車後沿階梯一路走上去,很漂亮。City walker千萬不能錯過。雖然週一貌似展廳沒有開,但是還是看到不少東西。有一段路鋪滿了鮮花。下午去了Bupyeongdong 1(il)-ga,炸雞和啤酒果然很搭。在南浦洞地下又買了幾件衣服,這裏是真便宜。最後坐8路去了太宗臺。太宗臺人很多,我們來這麼多天,這裏人最多。可惜天氣沒有晴朗的可以看到馬島的程度。但石灘還是很美的。101直接會到酒店附近,下決心吃了米腸和一些白水煮的內臟,也是很好吃的,沒有奇怪的味道。看到路邊有賣大腦瓜的,很想買,可是人家只一籃一藍賣。

05日。12點在7thStreetPizza吃了土豆红薯披萨,然後地鐵轉輕鐡去機場,很順利。飛機晚點了一會兒,最後完全離開香港機場的時間六點半。S56站牌上說到東沖碼頭,但是不知爲什麼沒有停。我們在海堤灣畔下的,往回走了一小段。渡輪19:30從東涌碼頭出發,逗了會兒碼頭冷酷的貓。在蝴蝶廣場吃了一粥麵,坐B3回到深圳灣,最後到家的時間是22:24。


其他

我們在Booking上訂的酒店都不錯。華夏銀行卡境外取款每日首筆免手續費。香港韓國7天5G流量卡足夠用,我最後用了不到1G。公交卡7-11就有買,儘早買。單程優惠100,且可享連程(30分鐘內)優惠。釜山公交一票制,如果不需連程則下車不必刷卡。下公交前按鈴示意司機,否則不停車。飯館基本不光沒中英文,甚至沒有圖片。Google Translate可以拍照翻譯,效果一般。MAPS.ME和Google Map搭配,地圖基本沒問題,再有就是參考當地的旅遊冊子了。啤酒一定要配辣火鍋或烤肉或炸雞才過癮,直接喝一瓶冰鎮啤酒是沒有那麼好喝的。我們要去哪裏都是前一晚決定的,事實證明這樣也很不錯。除了景點釜山和慶州都有太多地方可以去,像慶州的河邊、民主公園附近、海雲臺火車站附近的小河,都非常漂亮。語言方面不用糾結,又不是去談生意,多比劃一會兒總能明白的,也是一種另類體驗。關東煮的湯,山上的水附近都有水瓢,是公用的,用完放回即可。泡菜幾乎頓頓有,好吃是好吃,但是真辣啊。我們去的這個季節,慶州都處都能見到艾草,大蔥(已長蔥筆),燕麥,蒲公英和蒜苗。小食品都是小時候的味道。酒店都有提供電腦,但是是漢語的Win7系統。一臺裝了Ghost,重啓後會還原系統,另一臺用戶沒有管理員權限,所以都無法更改語言。電腦重度依賴者可以考慮帶個Ubuntu優盤過去。韓國網速真的很快!街上沒有垃圾桶。貓啊狗啊鳥啊都很肥,且不怕人。自拍幹、鏡子、彩色塑料水箱以及泡菜罈子非常多。

本文更新於 2015/05/11。

分類
說說

分享一個下電影的站點——藍影網

藍影網裏面資源挺多,老片新片都有,清晰度也都不錯。最近《權利的遊戲》第五季開播,前幾季都是在人人影視下的,去年人人影視被關了。現在可以到藍影網下。

分類
网站

使用laravel5自帶的用戶授權系統

我這個不是教程,只是記錄。目標是修改laravel5默認的用戶系統,使之能迅速使用。註冊時在原有信息基礎上添加手機號。至於搭建laravel看參見:在ubuntu上部署apache和laravel5

首先修改數據庫,添加phone列。由於我數據庫還是空的所以就先php artisan migrate:rollback,然後在database/migrations/2014_10_12_000000_create_users_table.php up函數中添加一行

$table->string('phone')->unique();

然後執行php artisan migrate這樣用戶數據庫就有phone了,而且phone還是唯一的。然後修改app/Http/Controllers/Auth/AuthController.php,主要是重載postRegister和postLogin兩個函數。原函數可從vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesAndRegistersUsers.php中複製過來,修改後是這樣的:

//app/Http/Controllers/Auth/AuthController.php
<?php namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\Registrar;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Http\Request;

class AuthController extends Controller {



	use AuthenticatesAndRegistersUsers;
        //修改跳轉地址
        protected $redirectTo = '/';


	public function __construct(Guard $auth, Registrar $registrar)
	{
		$this->auth = $auth;
		$this->registrar = $registrar;

		$this->middleware('guest', ['except' => 'getLogout']);
	}

	public function postRegister(Request $request)
	{       
                //數據驗證
                $this->validate($request, ['phone'=>'required|numeric|unique:users',);
		$validator = $this->registrar->validator($request->all());

		if ($validator->fails())
		{
			$this->throwValidationException(
				$request, $validator
			);
		}

		$this->auth->login($this->registrar->create($request->all()));

		return redirect($this->redirectPath());
	}
        
        //這裏修改爲可以通過郵箱或手機登陸
        public function postLogin(Request $request)
	{
		
                $field = filter_var($request->input('login'), FILTER_VALIDATE_EMAIL) ? 'email' : 'phone';
                $request->merge([$field => $request->input('login')]);
                $this->validate($request, [
			'login' => 'required', 'password' => 'required',
		]);

		$credentials = $request->only($field, 'password');

		if ($this->auth->attempt($credentials, $request->has('remember')))
		{
			return redirect()->intended($this->redirectPath());
		}

		return redirect($this->redirectPath())
					->withInput($request->only('email', 'remember'))
					->withErrors([
						'email' => $this->getFailedLoginMessage(),
					]);
	}
	protected function getFailedLoginMessage()
	{
		return '郵箱或手機與密碼不匹配。';
	}

}

然後修改下位於/home/zenggl/case1/resources/views/auth下的登陸和註冊的表單,登錄表單修改一下email輸入框的name爲login,註冊表單添加一個name爲phone的輸入框。

密碼重置功能laravel5中也是實現好的,只用配置一下郵件發送就好了,我以QQ企業郵箱爲例配置。首先在.env中填入郵件服務器信息,然後設置config中的mail就好了:

//.env
MAIL_DRIVER=smtp
MAIL_HOST=smtp.exmail.qq.com
MAIL_PORT=465
[email protected]
MAIL_PASSWORD=yourPassword
//config/mail.php
//視郵箱服務器而定,QQ郵箱要用ssl加密,且發信人要和發信帳號一致
'from' => ['address' => '[email protected]', 'name' => null],
'encryption' => 'ssl',

修改提示語言爲簡體中文。首先修改config/app.index中的locale值爲zh-cmn-Hans,然後複製一份resources/lang/en並重命名爲zh-cmn-Hans,最後修改validation.php就可以了。