多线程的cURL

WBOY
Freigeben: 2016-07-25 08:49:34
Original
975 Leute haben es durchsucht
本处是一个可以灵活的多线程的调用 cURL的。
这里跟php手册 http://us2.php.net/manual/zh/function.curl-multi-select.php 中提供的样例不同,代码执行效率要高不少
本处两个文件,一个是muti_curl的文件,里面包含两个class
一个是运用的方法,这里是批量检查代理ip是否可用
  1. class request_setting {
  2. public $url = false;
  3. public $method = 'GET';
  4. public $post_data = null;
  5. public $headers = null;
  6. public $options = null;
  7. function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
  8. $this->url = $url;
  9. $this->method = $method;
  10. $this->post_data = $post_data;
  11. $this->headers = $headers;
  12. $this->options = $options;
  13. }
  14. public function __destruct() {
  15. unset($this->url, $this->method, $this->post_data, $this->headers, $this->options);
  16. }
  17. }
  18. /***********************************************************************************************
  19. 批量操作的类
  20. *******************************************************************************************/
  21. class muti_curl {
  22. protected $thread_size = 100;
  23. protected $timeout = 30;
  24. private $callback;
  25. protected $options = array(
  26. CURLOPT_SSL_VERIFYPEER => false,//禁用后cURL将终止从服务端进行验证。使用CURLOPT_CAINFO选项设置证书使用CURLOPT_CAPATH选项设置证书目录 如果CURLOPT_SSL_VERIFYPEER(默认值为2)被启用,CURLOPT_SSL_VERIFYHOST需要被设置成TRUE否则设置为FALSE。 自cURL 7.10开始默认为TRUE。从cURL 7.10开始默认绑定安装。
  27. CURLOPT_RETURNTRANSFER => true, //将 curl_exec()获取的信息以文件流的形式返回,而不是直接输
  28. CURLOPT_CONNECTTIMEOUT => 15,
  29. CURLOPT_TIMEOUT => 30,
  30. // CURLOPT_HTTP_VERSION=>CURL_HTTP_VERSION_1_0, //使用代理的时候用这个去抓取数据,更爽
  31. // CURLOPT_AUTOREFERER=>false,// 当根据Location:重定向时,自动设置header中的Referer:信息。
  32. // CURLOPT_BINARYTRANSFER=>false, //在启用CURLOPT_RETURNTRANSFER的时候,返回原生的(Raw)输出这个不用设置。
  33. // CURLOPT_COOKIESESSION=>true,// 启用时curl会仅仅传递一个session cookie,忽略其他的cookie,默认状况下cURL会将所有的cookie返回
  34. // CURLOPT_CRLF=>false,// 启用时将Unix的换行符转换成回车换行符。
  35. // CURLOPT_DNS_USE_GLOBAL_CACHE=>false, // 启用时会启用一个全局的DNS缓存,此项为线程安全的,并且默认启用。
  36. // CURLOPT_FAILONERROR=>false, // 显示HTTP状态码,默认行为是忽略编号小于等于400的HTTP信息。
  37. // CURLOPT_FILETIME=>true, //启用时会尝试修改远程文档中的信息。结果信息会通过 curl_getinfo()函数的CURLINFO_FILETIME选项返回。 curl_getinfo().
  38. // CURLOPT_FOLLOWLOCATION=>false, // 启用时会将服务器服务器返回的"Location: "放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。
  39. // CURLOPT_FORBID_REUSE=>true, //在完成交互以后强迫断开连接,不能重用。
  40. // CURLOPT_FRESH_CONNECT=>true,// 强制获取一个新的连接,替代缓存中的连接。
  41. // CURLOPT_FTP_USE_EPRT=>false,// 启用时当FTP下载时,使用EPRT (或 LPRT)命令。设置为FALSE时禁用EPRT和LPRT,使用PORT命令 only.
  42. // CURLOPT_FTP_USE_EPSV=>false,// 启用时,在FTP传输过程中回复到PASV模式前首先尝试EPSV命令。设置为FALSE时禁用EPSV命令。
  43. // CURLOPT_FTPAPPEND=>false,// 启用时追加写入文件而不是覆盖它。
  44. // CURLOPT_FTPASCII=>false,// CURLOPT_TRANSFERTEXT的别名。
  45. // CURLOPT_FTPLISTONLY=>false,// 启用时只列出FTP目录的名字。
  46. // CURLOPT_HEADER=>true,// 启用时会将头文件的信息作为数据流输出。
  47. // CURLINFO_HEADER_OUT=>false, //启用时追踪句柄的请求字符串。
  48. // CURLOPT_HTTPGET=>true,// 启用时会设置HTTP的method为GET,因为GET是默认是,所以只在被修改的情况下使用。
  49. // CURLOPT_HTTPPROXYTUNNEL =>true,// 启用时会通过HTTP代理来传输。
  50. // CURLOPT_MUTE=>true,// 启用时将cURL函数中所有修改过的参数恢复默认值。
  51. // CURLOPT_NETRC=>false,// 在连接建立以后,访问~/.netrc文件获取用户名和密码信息连接远程站点。
  52. // CURLOPT_NOBODY=>true, 启用时将不对HTML中的BODY部分进行输出。
  53. // CURLOPT_NOPROGRESS=>false,//启用时关闭curl传输的进度条,此项的默认设置为启用。
  54. // CURLOPT_NOSIGNAL=>false,// 启用时忽略所有的curl传递给php进行的信号。在SAPI多线程传输时此项被默认启用。 cURL 7.10时被加入。
  55. // CURLOPT_POST=>false,// 启用时会发送一个常规的POST请求,类型为:application/x-www-form-urlencoded,就像表单提交的一样。
  56. // CURLOPT_PUT=>false,// 启用时允许HTTP发送文件,必须同时设置CURLOPT_INFILE和CURLOPT_INFILESIZE。
  57. // CURLOPT_TRANSFERTEXT=>false,// 启用后对FTP传输使用ASCII模式。对于LDAP,它检索纯文本信息而非HTML。在Windows系统上,系统不会把STDOUT设置成binary模式。
  58. // CURLOPT_UNRESTRICTED_AUTH=>true,// 在使用CURLOPT_FOLLOWLOCATION产生的header中的多个locations中持续追加用户名和密码信息,即使域名已发生改变。
  59. // CURLOPT_UPLOAD=>false,// 启用后允许文件上传。
  60. // CURLOPT_VERBOSE =>true,// 启用时会汇报所有的信息,存放在STDERR或指定的CURLOPT_STDERR中。
  61. );
  62. private $headers = array();
  63. private $requests = array();
  64. private $requestMap = array();
  65. /*********************
  66. 构造一个callback的函数
  67. ********************/
  68. function __construct($callback = null) {
  69. $this->callback = $callback;
  70. }
  71. /********************************************************************
  72. 重载 __get的方法
  73. *******************************************************************/
  74. public function __get($name) {
  75. return (isset($this->{$name})) ? $this->{$name} : null;
  76. }
  77. /*********************************************************************
  78. 重载 __set的方法
  79. *******************************************************/
  80. public function __set($name, $value) {
  81. // 增加一个设置到headers
  82. if ($name == "options" || $name == "headers") {
  83. $this->{$name} = $value + $this->{$name};
  84. } else {
  85. $this->{$name} = $value;
  86. }
  87. return true;
  88. }
  89. //增加一个请求
  90. public function add($request) {
  91. $this->requests[] = $request;
  92. return true;
  93. }
  94. public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
  95. $this->requests[] = new request_setting($url, $method, $post_data, $headers, $options);
  96. return true;
  97. }
  98. public function get($url, $headers = null, $options = null) {
  99. return $this->request($url, "GET", null, $headers, $options);
  100. }
  101. public function post($url, $post_data = null, $headers = null, $options = null) {
  102. return $this->request($url, "POST", $post_data, $headers, $options);
  103. }
  104. private function single_curl() {
  105. $ch = curl_init(); //初始化
  106. $request = array_shift($this->requests);//把第一个单元移出并作为结果
  107. $options = $this->get_options($request);//获得该单元的设置
  108. curl_setopt_array($ch, $options);//批设置
  109. $output = curl_exec($ch);
  110. $curl_info = curl_getinfo($ch);
  111. if ($this->callback) {
  112. $callback = $this->callback;
  113. if (is_callable($this->callback)) {
  114. call_user_func($callback, $output, $curl_info, $request);
  115. }
  116. }
  117. else
  118. return $output;
  119. return true;
  120. }
  121. private function rolling_curl($thread_size = null) {
  122. if ($thread_size){
  123. $this->thread_size = $thread_size;
  124. }
  125. if (count($this->requests) thread_size){
  126. $this->thread_size = count($this->requests);
  127. }
  128. if ($this->thread_size $errorinfo = '线程大小必须大于 1!!!!';
  129. throw new Exception($errorinfo);
  130. }
  131. $queue = curl_multi_init();
  132. //在线程里开始增加任务队列
  133. for ($i = 0; $i thread_size; $i++) {
  134. $ch = curl_init();
  135. $options = $this->get_options($this->requests[$i]);
  136. curl_setopt_array($ch, $options);//获得设置
  137. curl_multi_add_handle($queue, $ch);//添加进去
  138. $key = (string) $ch;
  139. $this->requestMap[$key] = $i;
  140. }
  141. do {
  142. while (($statu_run_muti_exec = curl_multi_exec($queue, $active)) == CURLM_CALL_MULTI_PERFORM) ;
  143. if ($statu_run_muti_exec != CURLM_OK){ break; }
  144. // 发现完成的一个请求,进行处理
  145. while ($done = curl_multi_info_read($queue)) {
  146. $curl_info = curl_getinfo($done['handle']);
  147. $output = curl_multi_getcontent($done['handle']);
  148. $callback = $this->callback;
  149. if (is_callable($callback)){
  150. $key = (string) $done['handle'];
  151. $request = $this->requests[$this->requestMap[$key]];
  152. unset($this->requestMap[$key]);//这个销毁变量用得很帅
  153. call_user_func($callback, $output, $curl_info, $request);
  154. }
  155. //增加一个未处理的请求加入到一个已经完成的队列中
  156. if ($i requests) && isset($this->requests[$i]) && $i requests)) {
  157. $ch = curl_init();
  158. $options = $this->get_options($this->requests[$i]);
  159. curl_setopt_array($ch, $options);
  160. curl_multi_add_handle($queue, $ch);
  161. $key = (string) $ch;
  162. $this->requestMap[$key] = $i;
  163. $i++;
  164. }
  165. curl_multi_remove_handle($queue, $done['handle']);
  166. echo "done ";
  167. print_r($queue);
  168. print_r ($done);
  169. }
  170. // 这一步非常非常重要如果有一个完成后,要重新设置设置timeout的时间
  171. //这里很关键的一点,就是要保证第一次所有的线程里的请求最少有一个是有效的,否则就出现第一次所有的都没有效果,导致 $active=0,因此不执行下面的东西
  172. if ($active >0){
  173. curl_multi_select($queue, $this->timeout);
  174. }
  175. } while ($active);
  176. curl_multi_close($queue);
  177. return true;
  178. }
  179. public function execute($thread_size = null) {
  180. //判断thread_size的大小如果只有一个请求,就用单线程模式
  181. if (count($this->requests) == 1) {
  182. return $this->single_curl();
  183. } else {
  184. return $this->rolling_curl($thread_size);
  185. }
  186. }
  187. private function get_options($request) {
  188. $options = $this->__get('options');
  189. if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
  190. // $options[CURLOPT_FOLLOWLOCATION] = 1;
  191. // $options[CURLOPT_MAXREDIRS] = 5;
  192. }
  193. $headers = $this->__get('headers');
  194. if ($request->options) {
  195. $options = $request->options + $options;
  196. }
  197. $options[CURLOPT_URL] = $request->url;
  198. //下面分别对post选项与header选项进行设置
  199. if ($request->post_data){
  200. $options[CURLOPT_POST] = 1;
  201. $options[CURLOPT_POSTFIELDS] = $request->post_data;
  202. }
  203. if ($headers) {
  204. $options[CURLOPT_HEADER] = 0;
  205. $options[CURLOPT_HTTPHEADER] = $headers;
  206. }
  207. return $options;
  208. }
  209. public function __destruct() {
  210. unset($this->thread_size, $this->callback, $this->options, $this->headers, $this->requests);
  211. }
  212. }
  213. ?>
复制代码
  1. header("content-type:text/html; charset=utf-8");
  2. require("muti_curl_class.php");
  3. set_time_limit(0);
  4. $sucesesnum=0;
  5. $good_proxy=array();
  6. function request_callback($response, $info, $request) {
  7. global $sucesesnum,$good_proxy;
  8. // 下面的正则可以对返回的结果进行选择性显示
  9. /* if (preg_match("~(.*?)~i", $response, $out)) {
  10. $title = $out[1];
  11. }*/
  12. // echo '
    '.$response .'
    ';
  13. echo '
    ';
  14. //对响应也就是 $response 进行检测判断里面是否有设定的字符,如果有判断运用该代理成功
  15. if( $response !== false && substr_count($response, 'User-agent: Baiduspider') >=1 ) {
  16. // $result = true;
  17. echo "true
    ";
  18. // echo $request[options][10004];
  19. // print_r ($request->options);
  20. echo $request->options[CURLOPT_PROXY];
  21. $good_proxy[]=$request->options[CURLOPT_PROXY];
  22. }
  23. echo '
    the-->'. $sucesesnum.'// print_r ($request);
  24. //echo $request->url;
  25. $sucesesnum++;
  26. echo "
    ";
  27. }
  28. $params = array_merge($_GET, $_POST); //此处获得传递过来的代理ip的地址
  29. $result = $proxy_ip = trim($params['ip']);
  30. $timeout=intval(trim($params['timeout']));
  31. if($timeoutif($timeout>300){$timeout=300;}
  32. $thread_size=intval(trim($params['thread_size']));
  33. if($thread_sizeif($thread_size>300){$thread_size =300;}
  34. if($proxy_ip == '') {
  35. echo '请输入IP!!';
  36. return;
  37. }
  38. $replace_arr1 = array(' ', 'qq代理:', 'dn28.com', 'qqip', 'qq代理', 'qq代理ip', '代理ip:', 'ip:', '代理ip','"',"'",'\\','/',' ');
  39. $result = str_replace($replace_arr1, array(''), $result);
  40. $result = str_replace(",", "\n", $result);
  41. $resArr = explode("\n", $result);
  42. foreach($resArr as $k => $v) {
  43. $posProxy = getPos($v, '@');
  44. if($posProxy===false){
  45. if (!empty($v)){$proxyip_and_port = $v; }
  46. }else{
  47. $proxyip_and_port = substr($v, 0, $posProxy);
  48. }
  49. $newRes[] =trim($proxyip_and_port);
  50. }
  51. print_r($newRes);
  52. //die();
  53. $option_setting = array(
  54. CURLOPT_SSL_VERIFYPEER => 0,
  55. CURLOPT_RETURNTRANSFER => true,
  56. CURLOPT_CONNECTTIMEOUT => 5,
  57. CURLOPT_TIMEOUT => 30,
  58. CURLOPT_HEADER=>false,
  59. CURLOPT_PROXY=>'',//这个地方设置代理的位置
  60. );
  61. $url= 'http://www.baidu.com/robots.txt';
  62. $btime=time();
  63. $rc = new muti_curl("request_callback");
  64. $rc->timeout = $timeout;
  65. $rc->thread_size = $thread_size;
  66. foreach ($newRes as $v) {
  67. $option_setting[CURLOPT_PROXY]=$v;
  68. $request = new request_setting($url, $method = "GET", $post_data = null,$header= null, $option_setting);
  69. $rc->add($request);
  70. }
  71. $rc->execute();
  72. $etime=time();
  73. $usedtime=$etime-$btime;
  74. echo 'all'. $sucesesnum.'use'. $usedtime;
  75. echo '
    ';
  76. $good_proxy= array_unique($good_proxy);
  77. $str='';
  78. foreach ($good_proxy as $v){
  79. $str.="'".trim($v)."',";
  80. }
  81. $str= str_replace ( ' ' , '' ,$str );
  82. $str = preg_replace('/\s+/', ' ', $str);
  83. echo $str.'
    ';
  84. var_export ($good_proxy);
  85. //var_dump ($good_proxy);
  86. //**************************************************************************************************
  87. //*******************************只用了一个函数
  88. function parseProxyInfo ( $proxyStr ) {
  89. //$proxyStr = '202.115.207.25:80@HTTP;四川省成都市 四川师范大学';
  90. $posIp = getPos($proxyStr, ':');
  91. $ip = substr($proxyStr, 0, $posIp);
  92. $posPort = getPos($proxyStr, '@');
  93. $port = substr($proxyStr, $posIp+1, $posPort-$posIp-1);
  94. $posType = getPos($proxyStr, ';');
  95. $type = substr($proxyStr, $posPort+1, $posType-$posPort-1);
  96. $location = substr(strstr($proxyStr, ';'), 1);
  97. return array(
  98. 'ip' => $ip,
  99. 'port' => $port,
  100. 'type' => $type,
  101. 'location' => $location
  102. );
  103. }
  104. function getPos($haystack, $needle){
  105. return strpos($haystack, $needle);
  106. }
  107. function check_proxy_is_useful($model, $proxy_info_arr = array()) {
  108. global $params, $config;
  109. if($model == 'single') {
  110. $proxy_port = intval(trim($params['port']));
  111. $check_proxy_url = $config['verify_url'];
  112. $proxy_time_out = intval(trim($params['timeout']));
  113. $retry = intval(trim($params['retry']));
  114. $proxy_ip = trim($params['ip']);
  115. $proxy = new proxy( $proxy_ip, $proxy_port, $check_proxy_url, $proxy_time_out, $retry );
  116. //成功返回string success, 失败返回boolean false
  117. $result = $proxy -> check_proxy();
  118. //var_dump($result);
  119. $proxy_str_success = ''.$proxy_ip.':'.$proxy_port.'@'.'HTTP 代理验证成功!';
  120. $proxy_str_failed = ''.$proxy_ip.':'.$proxy_port.'@'.'HTTP 代理验证失败!';
  121. return $result !== false ? $proxy_str_success : $proxy_str_failed;
  122. } elseif ($model == 'collect') {
  123. $proxy_port = intval(trim($proxy_info_arr['port']));
  124. $check_proxy_url = $config['verify_url'];
  125. $proxy_time_out = intval(trim($params['timeout']));
  126. $retry = intval(trim($params['retry']));
  127. $proxy_ip = trim($proxy_info_arr['ip']);
  128. /*echo $proxy_ip.'
    ';
  129. echo $proxy_port.'
    ';
  130. echo $check_proxy_url.'
    ';
  131. echo $proxy_time_out.'
    ';
  132. echo $retry.'
    ';*/
  133. if(!isset($proxy)) {
  134. $proxy = new proxy( $proxy_ip, $proxy_port, $check_proxy_url, $proxy_time_out, $retry );
  135. }
  136. //成功返回string success, 失败返回boolean false
  137. $result = $proxy -> check_proxy();
  138. return $result;
  139. }
  140. }
  141. function get_single(){
  142. global $params, $config;
  143. $proxy_ip = trim($params['ip']);
  144. if($proxy_ip == '') {
  145. echo '请输入IP!!';
  146. return;
  147. }
  148. echo check_proxy_is_useful('single');
  149. }
  150. function get_proxy_by_collect(){
  151. global $params, $config;
  152. $params['url'] = trim($params['url']);
  153. if($params['url'] == '') {
  154. echo '请输入url!';
  155. return;
  156. }
  157. //$url = 'http://www.dn28.com/html/75/n-5175.html';
  158. $con = iconv('GBK', 'UTF-8', file_get_contents($params['url']));
  159. preg_match ('/(.*)
    /s', $con, $arr);
  160. $result = strip_tags ($arr[1], '
    ');
  161. $replace_arr1 = array(' ', 'qq代理:', 'dn28.com', 'qqip', 'qq代理', 'qq代理ip', '代理ip:', 'ip:', '代理ip');
  162. $result = str_replace($replace_arr1, array(''), $result);
  163. //print_r($arr);
  164. $resArr = explode('
    ', $result);
  165. //print_r($resArr);
  166. echo '

    代理开始批量验证中,整个过程将会花费您几分钟时间。

    ';
  167. unset($_SESSION['success_arr']);
  168. foreach($resArr as $k => $v) {
  169. $newRes[$k] = parseProxyInfo($v);
  170. //print_r($newRes[$k]);
  171. /*return;*/
  172. $result = check_proxy_is_useful('collect', $newRes[$k]);
  173. $proxy_str_success = ''.$newRes[$k]['ip'].':'.$newRes[$k]['port'].'@'.$newRes[$k]['type'].' 代理验证成功!   IP地址:'.$newRes[$k]['location'].'';
  174. $proxy_str_failed = ''.$newRes[$k]['ip'].':'.$newRes[$k]['port'].'@'.$newRes[$k]['type'].' 代理验证失败!   IP地址:'.$newRes[$k]['location'].'';
  175. if($result !== false ){
  176. echo $proxy_str_success;
  177. $_SESSION['success_arr'][] = $success_arr[] = $newRes[$k];
  178. } else {
  179. echo $proxy_str_failed;
  180. }
  181. echo '
    ';
  182. }
  183. if(isset($success_arr) && count($success_arr) > 0 ) {
  184. save_success_proxy($success_arr);
  185. echo '

    [保存验证成功的代理到本地电脑]   [我要看看历史数据]

    ';
  186. } else {
  187. echo '

    [我要看看历史数据]

    ';
  188. }
  189. //print_r($success_arr);
  190. }
  191. function get_proxy_by_rule(){
  192. global $params, $config;
  193. $result = $proxy_ip = trim($params['ip']);
  194. if($proxy_ip == '') {
  195. echo '请输入IP!!';
  196. return;
  197. }
  198. $replace_arr1 = array(' ', 'qq代理:', 'dn28.com', 'qqip', 'qq代理', 'qq代理ip', '代理ip:', 'ip:', '代理ip');
  199. $result = str_replace($replace_arr1, array(''), $result);
  200. $resArr = explode("\n", $result);
  201. //print_r($resArr);
  202. echo '

    代理开始批量验证中,整个过程将会花费您几分钟时间。

    ';
  203. unset($_SESSION['success_arr']);
  204. foreach($resArr as $k => $v) {
  205. $newRes[$k] = parseProxyInfo($v);
  206. //print_r($newRes[$k]);
  207. /*return;*/
  208. $result = check_proxy_is_useful('collect', $newRes[$k]);
  209. //var_dump($result);
  210. $proxy_str_success = ''.$newRes[$k]['ip'].':'.$newRes[$k]['port'].'@'.$newRes[$k]['type'].' 代理验证成功!   IP地址:'.$newRes[$k]['location'].'';
  211. $proxy_str_failed = ''.$newRes[$k]['ip'].':'.$newRes[$k]['port'].'@'.$newRes[$k]['type'].' 代理验证失败!   IP地址:'.$newRes[$k]['location'].'';
  212. if($result !== false ){
  213. echo $proxy_str_success;
  214. $_SESSION['success_arr'][] = $success_arr[] = $newRes[$k];
  215. } else {
  216. echo $proxy_str_failed;
  217. }
  218. echo '
    ';
  219. }
  220. if(isset($success_arr) && count($success_arr) > 0 ) {
  221. save_success_proxy($success_arr);
  222. echo '

    [保存到php格式文件]   [保存验证成功的代理到本地电脑]   [我要看看历史数据]

    ';
  223. } else {
  224. echo '

    [我要看看历史数据]

    ';
  225. }
  226. }
  227. function save_success_proxy($success_arr){
  228. global $config;
  229. date_default_timezone_set('PRC');
  230. $str = '';
  231. foreach($success_arr as $k => $v) {
  232. $str .= $v['ip'].':'.$v['port'].'@'.$v['type'].';'.$v['location']."\n";
  233. }
  234. $fp = fopen($config['root_path'].'/success_proxy/'.date('YmdHi').'.log', 'a+');
  235. fwrite($fp, $str);
  236. fclose($fp);
  237. unset($str);
  238. }
  239. ?>
复制代码


Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage